PowerShell/src/Microsoft.PackageManagement.MetaProvider.PowerShell/PowerShellMetaProvider.cs
PowerShell Team c748652c34 Copy all mapped files from [SD:725290]
commit 8cec8f150da7583b7af5efbe2853efee0179750c
2016-07-28 23:23:03 -07:00

830 lines
37 KiB
C#

//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace Microsoft.PackageManagement.MetaProvider.PowerShell.Internal {
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.PackageManagement.Internal.Utility.Extensions;
using Microsoft.PackageManagement.Internal.Utility.Versions;
using Microsoft.PackageManagement.Internal.Utility.Collections;
using System.Globalization;
using Implementation;
using PackageManagement.Internal;
using PackageManagement.Internal.Implementation;
using PackageManagement.Internal.Providers;
using PackageManagement.Internal.Utility.Plugin;
using ErrorCategory = System.Management.Automation.ErrorCategory;
using ProviderOption = PackageManagement.Internal.Implementation.PackageManagementService.ProviderOption;
internal class ProviderItem {
internal PSModuleInfo ModuleInfo;
internal string ProviderPath;
}
/// <summary>
/// A MetaProvider class that loads Providers implemented as a PowerShell Module.
/// It connects the functions in the PowerShell module to the expected functions that the
/// interface expects.
/// </summary>
public class PowerShellMetaProvider : IDisposable {
private static readonly HashSet<string> _exclusionList = new HashSet<string> {
"AppBackgroundTask",
"AppLocker",
"Appx",
"AssignedAccess",
"BitLocker",
"BitsTransfer",
"BranchCache",
"CimCmdlets",
"Defender",
"DirectAccessClientComponents",
"Dism",
"DnsClient",
"Hyper-V",
"International",
"iSCSI",
"ISE",
"Kds",
"Microsoft.PowerShell.Diagnostics",
"Microsoft.PowerShell.Host",
"Microsoft.PowerShell.Management",
"Microsoft.PowerShell.Security",
"Microsoft.PowerShell.Utility",
"Microsoft.WSMan.Management",
"MMAgent",
"MsDtc",
"NetAdapter",
"NetConnection",
"NetEventPacketCapture",
"NetLbfo",
"NetNat",
"NetQos",
"NetSecurity",
"NetSwitchTeam",
"NetTCPIP",
"NetWNV",
"NetworkConnectivityStatus",
"NetworkTransition",
"PcsvDevice",
"PKI",
"PrintManagement",
"PSDiagnostics",
"PSScheduledJob",
"PSWorkflow",
"PSWorkflowUtility",
"ScheduledTasks",
"SecureBoot",
"SmbShare",
"SmbWitness",
"StartScreen",
"Storage",
"TLS",
"TroubleshootingPack",
"TrustedPlatformModule",
"VpnClient",
"Wdac",
"WindowsDeveloperLicense",
"WindowsErrorReporting",
"WindowsSearch",
"PackageManagement", // dont' search ourselves.
"OneGet", // dont' search ourselves.
"OneGet-Edge" // dont' search ourselves.
};
private static string _baseFolder;
private static string _powershellProviderFunctionsPath;
//The reason of using 'object' instead of' PowerShellPackageProvider' is that PowerShellPackageProvider is a provider
//that is not visiable to the PackageManagment.
private readonly IDictionary<string, object> _availableProviders = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private readonly IDictionary<string, List<ProviderItem>> _psProviderCacheTable = new Dictionary<string, List<ProviderItem>>(StringComparer.OrdinalIgnoreCase);
internal const string PowerShellGet = "PowerShellGet";
private static readonly PowerShell _powershell = PowerShell.Create();
private static bool _initialized = false;
public PowerShellMetaProvider() {
// _packageProviders.BlockingEnumerator = true;
}
internal static string BaseFolder {
get {
if (_baseFolder == null) {
#if CORECLR
_baseFolder = Path.GetDirectoryName(Path.GetFullPath(typeof(PowerShellMetaProvider).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName));
#else
_baseFolder = Path.GetDirectoryName(Path.GetFullPath(Assembly.GetExecutingAssembly().Location));
#endif
if (_baseFolder == null || !Directory.Exists(_baseFolder)) {
throw new Exception(Resources.Messages.CantFindBasePowerShellModuleFolder);
}
}
return _baseFolder;
}
}
static PowerShellMetaProvider() {
try {
EnsurePowerShellInitialized();
} catch {
//we capture the exceptions here so that we won't fail to load the metaprovider assembly
//exceptions can happen when execution-policy is set to restrict, for example.
}
}
static void EnsurePowerShellInitialized() {
if (!_initialized) {
_powershell.ImportModule(PowerShellProviderFunctions);
_initialized = true;
}
}
internal static string PowerShellProviderFunctions {
get {
if (_powershellProviderFunctionsPath == null) {
// try the etc directory
_powershellProviderFunctionsPath = Path.Combine(BaseFolder, "etc", "PackageProviderFunctions.psm1");
if (!File.Exists(_powershellProviderFunctionsPath)) {
// fall back to the same directory.
_powershellProviderFunctionsPath = Path.Combine(BaseFolder, "PackageProviderFunctions.psm1");
if (!File.Exists(_powershellProviderFunctionsPath)) {
// oh-oh, no powershell functions file.
throw new Exception(String.Format(CultureInfo.CurrentCulture, Resources.Messages.UnableToFindPowerShellFunctionsFile, _powershellProviderFunctionsPath));
}
}
}
return _powershellProviderFunctionsPath;
}
}
public IEnumerable<string> ProviderNames {
get {
//return _packageProviders.Select(each => each.GetPackageProviderName());;
return _availableProviders.Keys;
}
}
private void AddToTable(string name, ProviderItem provider) {
//try to find if the provider is in the table already
if (_psProviderCacheTable.ContainsKey(name)) {
var list = _psProviderCacheTable[name];
var index = list.FindIndex(each => (each.ModuleInfo.Version == provider.ModuleInfo.Version) && (each.ProviderPath.EqualsIgnoreCase(provider.ProviderPath)));
if (index != -1) {
list[index] = provider;
} else {
_psProviderCacheTable[name].Add(provider);
}
} else {
var entry = new List<ProviderItem> {
provider
};
_psProviderCacheTable.Add(name, entry);
}
}
/// <summary>
/// The name of this MetaProvider class
/// </summary>
public string MetaProviderName {
get {
return "PowerShell";
}
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public string GetProviderPath(string providername) {
return _availableProviders.Keys.Where(each => each.EqualsIgnoreCase(providername))
.Select(each => ((PowerShellPackageProvider)_availableProviders[each]).ModulePath).FirstOrDefault();
}
private IEnumerable<KeyValuePair<string,PSModuleInfo>> ScanPrivateDataForProviders(PsRequest request, string baseFolder, Hashtable privateData, PSModuleInfo moduleInfo) {
var providers = privateData.GetStringCollection("PackageManagementProviders").ReEnumerable();
if (providers.Any()) {
// found a module that is advertizing one or more Providers.
foreach (var provider in providers) {
var fullPath = provider;
try {
if (!Path.IsPathRooted(provider)) {
fullPath = Path.GetFullPath(Path.Combine(baseFolder, provider));
}
} catch {
// got an error from the path.
continue;
}
if (Directory.Exists(fullPath) || File.Exists(fullPath)) {
// looks like we have something that could definitely be a
// a module path.
var result = new KeyValuePair<string, PSModuleInfo>(fullPath, moduleInfo);
AddToPowerShellProviderCacheTable(result);
yield return result;
} else {
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.FileNotFound, fullPath));
}
}
} else {
request.Debug(string.Format(Resources.Messages.PackageManagementProvidersNotFound, baseFolder));
}
}
private IEnumerable<KeyValuePair<string, PSModuleInfo>> GetPackageManagementModules(PsRequest request, PSModuleInfo module, Version requiredVersion, Version minimumVersion, Version maximumVersion) {
// skip modules that we know don't contain any PM modules
if (!_exclusionList.Contains(module.Name)) {
var privateData = module.PrivateData as Hashtable;
if (privateData != null) {
if (requiredVersion != null) {
if ((FourPartVersion)module.Version == (FourPartVersion)requiredVersion) {
return ScanPrivateDataForProviders(request, Path.GetDirectoryName(module.Path), privateData, module).ToArray();
} else {
return Enumerable.Empty<KeyValuePair<string, PSModuleInfo>>();
}
}
if ((minimumVersion != null) && ((FourPartVersion)module.Version < (FourPartVersion)minimumVersion)) {
return Enumerable.Empty<KeyValuePair<string, PSModuleInfo>>();
}
if ((maximumVersion != null) && ((FourPartVersion)module.Version > (FourPartVersion)maximumVersion)) {
return Enumerable.Empty<KeyValuePair<string, PSModuleInfo>>();
}
return ScanPrivateDataForProviders(request, Path.GetDirectoryName(module.Path), privateData, module).ToArray();
}
}
return Enumerable.Empty<KeyValuePair<string, PSModuleInfo>>();
}
private void AddToPowerShellProviderCacheTable(KeyValuePair<string, PSModuleInfo> moduleInfo) {
//some times when PrivateData in a provider's .psd1 meta file contains multiple providers (not recommanded way),
//they all reside under the same module path. So we extract each file name and add to the table as dictionary key
//to indicate they are actaully different providers.
if (moduleInfo.Key != null) {
var name = Path.GetFileNameWithoutExtension(moduleInfo.Key);
if (string.IsNullOrWhiteSpace(name)) {
return;
}
// for the case where provider is a dll, we enforce that provider name must be the same as module name
// so the name we add to the hash table will be the name of the module.
// (for the case where provider is not a dll, we still allow multiple provider names for a module)
if (string.Equals(Path.GetExtension(moduleInfo.Key), ".dll", StringComparison.OrdinalIgnoreCase))
{
name = moduleInfo.Value.Name;
}
AddToTable(name,
new ProviderItem {
ModuleInfo = moduleInfo.Value,
ProviderPath = moduleInfo.Key,
});
}
}
private static PackageManagementService PackageManagementService {
get {
return PackageManager.Instance as PackageManagementService;
}
}
//key = path, value = PSModuleInfo
private IEnumerable<KeyValuePair<string, PSModuleInfo>> ScanForModules(
PsRequest request,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
ProviderOption providerOption = ProviderOption.LatestVersion) {
// two places we search for modules
// 1. in this assembly's folder, look for all psd1 and psm1 files.
//
// 2. modules in the PSMODULEPATH
//
// Import each one of those, and check to see if they have a PackageManagementProviders section in their private data
var allAvailableModules = _powershell
.Clear()
.AddCommand("Get-Module")
.AddParameter("ListAvailable")
.Invoke<PSModuleInfo>();
return allAvailableModules.WhereNotNull()
.SelectMany(each => GetPackageManagementModules(request, each, requiredVersion, minimumVersion, maximumVersion));
}
private IEnumerable<KeyValuePair<string, PSModuleInfo>> ScanForPowerShellGetModule(PsRequest request)
{
var psget = _powershell.GetModule("PowerShellGet").FirstOrDefault();
return psget != null ? GetPackageManagementModules(request, psget, null, null, null) : Enumerable.Empty<KeyValuePair<string, PSModuleInfo>>();
}
private IEnumerable<string> AlternativeModuleScan(
PsRequest request,
Version requiredVersion,
Version minimumVersion,
Version maximumVersion,
ProviderOption providerOption = ProviderOption.LatestVersion) {
var psModulePath = Environment.GetEnvironmentVariable("PSModulePath") ?? "";
IEnumerable<string> paths = psModulePath.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries);
// add assumed paths just in case the environment variable isn't really set.
try {
#if CORECLR
paths = paths.ConcatSingleItem(Path.Combine(Environment.GetEnvironmentVariable("windir"), "system32", @"WindowsPowerShell\v1.0\Modules"));
#else
paths = paths.ConcatSingleItem(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"WindowsPowerShell\v1.0\Modules"));
#endif
} catch {
// skip the folder if it's not valid
}
try {
#if CORECLR
paths = paths.ConcatSingleItem(Path.Combine(Environment.GetEnvironmentVariable("userprofile"), "documents", @"WindowsPowerShell\Modules"));
#else
paths = paths.ConcatSingleItem(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"WindowsPowerShell\Modules"));
#endif
} catch {
// skip the folder if it's not valid
}
if (!string.IsNullOrWhiteSpace(BaseFolder) && BaseFolder.DirectoryExists()) {
paths = paths.ConcatSingleItem(BaseFolder);
}
var moduleFolders = paths.Distinct(new PathEqualityComparer(PathCompareOption.Full)).Where(each => each.DirectoryExists()).SelectMany(each => Directory.EnumerateDirectories(each).Where(dir => !_exclusionList.Contains(Path.GetFileName(dir))));
foreach (var module in moduleFolders) {
var moduleManifest = Path.Combine(module, Path.GetFileName(module) + ".psd1");
if (File.Exists(moduleManifest)) {
//The version check is defered in the GetPackageManagementModules() because we do not know the version without looking into the content.
yield return moduleManifest;
}
//The following are the cases where there are multiple modules installed on the system. The file folder is the module version.
var selectedVersions = Directory.EnumerateDirectories(module).Select(dir => new {
folder = dir,
ver = (FourPartVersion)Path.GetFileName(dir)
}).Where(each => each.ver > 0L);
if (requiredVersion != null) {
var version = selectedVersions.Where(each => each.ver == (FourPartVersion)requiredVersion).Select(each => each.folder).FirstOrDefault();
if (version != null) {
var file = Path.Combine(version, Path.GetFileName(Path.GetFileName(module)) + ".psd1");
if (File.Exists(file)) {
yield return file;
}
}
}
if (minimumVersion != null) {
selectedVersions = selectedVersions.Where(each => each.ver >= (FourPartVersion)minimumVersion);
}
if (maximumVersion != null) {
selectedVersions = selectedVersions.Where(each => each.ver <= (FourPartVersion)maximumVersion);
}
var results = (providerOption == PackageManagementService.ProviderOption.AllProvider) ?
selectedVersions.Select(version => Path.Combine(version.folder, Path.GetFileName(Path.GetFileName(module)) + ".psd1")).Where(File.Exists) :
new[] {
selectedVersions.OrderByDescending(each => each.ver)
.Select(version => Path.Combine(version.folder, Path.GetFileName(Path.GetFileName(module)) + ".psd1"))
.FirstOrDefault(File.Exists)
};
foreach (var result in results.WhereNotNull()) {
yield return result;
}
}
}
public object CreateProvider(string name) {
if (_availableProviders.ContainsKey(name)) {
var provider = _availableProviders[name];
if (provider != null) {
return provider;
}
}
// it's possible that this is a path passed in. Let's see if it's a provider.
if (!string.IsNullOrEmpty(name) && name.FileExists()) {
// MUST DO: load provider from filepath.
}
// create the instance
throw new Exception("No provider by name '{0}' registered.".format(name));
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
_availableProviders.Clear();
_psProviderCacheTable.Clear();
}
}
private PowerShellPackageProvider Create(PsRequest request, string modulePath, string requiredVersion, bool force, bool logWarning) {
var ps = PowerShell.Create();
try {
// load the powershell provider functions into this runspace.
if (ps.ImportModule(PowerShellProviderFunctions, false) != null) {
var result = ps.ImportModule(modulePath, force);
if (result != null) {
try {
return new PowerShellPackageProvider(ps, result, requiredVersion);
} catch (Exception e) {
e.Dump();
}
}
}
} catch (Exception e) {
// something didn't go well.
// skip it.
e.Dump();
if (logWarning) {
request.Warning(e.Message);
}
}
// didn't import correctly.
ps.Dispose();
return null;
}
public void InitializeProvider(PsRequest request) {
if (request == null) {
throw new ArgumentNullException("request");
}
request.Debug("Initializing PowerShell MetaProvider");
//During the initialization, we load the PowerShellGet only to speeding up a little bit
var psModules = ScanForPowerShellGetModule(request).WhereNotNull();
foreach (var psModule in psModules) {
//Check if the PowerShellGet provider exists
if ((psModule.Key != null) && (psModule.Value != null)) {
//load the PowerShellGet
AnalyzeModule(request, psModule.Key, psModule.Value.Version ?? new Version(0, 0), false, true, psModule.Value);
}
}
if (_availableProviders.ContainsKey(PowerShellGet))
{
request.Debug("Loaded PowerShell Provider: PowerShellGet");
}
else
{
//if we can not load PowerShellGet, we do not fail the initialization
request.Verbose(string.Format(CultureInfo.CurrentCulture, Resources.Messages.ModuleNotFound, PowerShellGet));
}
}
private void AddToPackageProviderCacheTable(PackageProvider provider)
{
PackageManagementService.AddToProviderCacheTable(provider.ProviderName, provider);
}
public void RefreshProviders(PsRequest request, string providerName, Version requiredVersion, Version minimumVersion, Version maximumVersion, bool logWarning) {
//find and load the latest versions of the providers if only providerName exists, e.g., get-pp -name or import-pp -name
//find and load the particular provider if both providerName and version are provided
_psProviderCacheTable.Clear();
EnsurePowerShellInitialized();
if (!string.IsNullOrWhiteSpace(providerName)) {
//Get the list of available providers
var modules = ScanForModules(request, requiredVersion, minimumVersion, maximumVersion, ProviderOption.AllProvider).ReEnumerable();
var tasks = modules.AsyncForEach(modulePath => AnalyzeModule(request, modulePath.Key, modulePath.Value.Version ?? new Version(0, 0), false, logWarning, modulePath.Value));
tasks.WaitAll();
} else {
//find all providers but only load the lastest if no name nor version exists, e.g. get-pp -list
//Scan for the all available providers
var results = ScanForModules(request, null, null, null, ProviderOption.AllProvider).ToArray();
if (!_psProviderCacheTable.Any()) {
return;
}
foreach (var list in _psProviderCacheTable.Values.WhereNotNull()) {
var psInfo = list.OrderByDescending(each => each.ModuleInfo.Version).ToArray();
if (!psInfo.Any()) {
continue;
}
PackageProvider pkgProvider = null;
for (var index = 0; index < psInfo.Length; index++) {
var providerItem = psInfo[index];
if (providerItem == null) {
continue;
}
// if the provider is a dll, we will just provide a default provider, assuming the module name will be the name of the provider and add to the cache table (if it is not already loaded)
if (string.Equals(Path.GetExtension(providerItem.ProviderPath), ".dll", StringComparison.OrdinalIgnoreCase))
{
// check whether it is already loaded or not
var loadedProvider = PackageManagementService.PackageProviders.FirstOrDefault(item => string.Equals(item.ProviderPath, providerItem.ProviderPath, StringComparison.OrdinalIgnoreCase));
// only provide default provider if it is not loaded
if (loadedProvider == null)
{
// here moduleinfo.providercategory is the module name.
// we are applying the guideline that module name is the same as provider name so we use that as the provider name
AddToPackageProviderCacheTable(new PackageProvider(new DefaultPackageProvider(providerItem.ModuleInfo.Name, providerItem.ModuleInfo.Version.ToString()))
{
ProviderPath = providerItem.ProviderPath,
Version = providerItem.ModuleInfo.Version,
IsLoaded = false
});
}
continue;
}
//load the provider that has the latest version
if (pkgProvider == null) {
// analyze the module
pkgProvider = AnalyzeModule(request, providerItem.ProviderPath, providerItem.ModuleInfo.Version, false, logWarning, providerItem.ModuleInfo);
} else {
//the rest of providers under the same module will just create a provider object for the output but not loaded
var packageProvider = new PackageProvider(new DefaultPackageProvider(pkgProvider.ProviderName, providerItem.ModuleInfo.Version.ToString()))
{
ProviderPath = providerItem.ProviderPath,
Version = providerItem.ModuleInfo.Version,
IsLoaded = false
};
AddToPackageProviderCacheTable(packageProvider);
}
}
}
_psProviderCacheTable.Clear();
}
}
private IEnumerable<PackageProvider> FindMatchedProvidersFromInternalCacheTable(PsRequest request, string providerPath) {
//Search from the internal table to see if we can the matched provider
var providers = PackageManagementService.ProviderCacheTable
.Select(each => each.Value).Select(list => list
.Where(each => each.ProviderPath.EqualsIgnoreCase(providerPath) && each.IsLoaded)
.Select(each => each));
return providers.SelectMany(list => {
var packageProviders = list as PackageProvider[] ?? list.ToArray();
return packageProviders;
});
}
public IEnumerable<object> LoadAvailableProvider(PsRequest request, string modulePath, Version requiredVersion, bool force) {
if (request == null) {
throw new ArgumentNullException("request");
}
if (string.IsNullOrEmpty(modulePath)) {
throw new ArgumentNullException("modulePath");
}
EnsurePowerShellInitialized();
//Check if it is already in the cache table
var providersAlreadyImported = FindMatchedProvidersFromInternalCacheTable(request, modulePath).ToArray();
if (providersAlreadyImported.Any() && !force) {
return providersAlreadyImported;
}
//Trying to load it from the path directly
var pkgProvider = AnalyzeModule(request, modulePath, requiredVersion ?? new Version(0, 0), force);
if (pkgProvider != null) {
return new[] { pkgProvider }.WhereNotNull();
}
request.Error(PackageManagement.Internal.Constants.Messages.FailedToImportProvider,
ErrorCategory.InvalidOperation.ToString(), PowerShellGet, string.Format(CultureInfo.CurrentCulture, Resources.Messages.FailedToImportProvider, modulePath));
return Enumerable.Empty<PackageProvider>();
}
private PackageProvider AnalyzeModule(PsRequest request, string modulePath, Version requiredVersion, bool force, bool logWarning =true, PSModuleInfo psModuleInfo = null)
{
if (string.IsNullOrWhiteSpace(modulePath)) {
return null;
}
request.Debug("Attempting to load PowerShell Provider Module [{0}]", modulePath);
if (string.Equals(Path.GetExtension(modulePath), ".dll", StringComparison.OrdinalIgnoreCase))
{
if (psModuleInfo != null)
{
// fake provider and returns it
var result = new PackageProvider(new DefaultPackageProvider(psModuleInfo.Name, psModuleInfo.Version.ToString()))
{
ProviderPath = modulePath,
Version = psModuleInfo.Version,
IsLoaded = false
};
AddToPackageProviderCacheTable(result);
return result;
}
else
{
// psmoduleinfo is only null when this function is called in loadavailableprovider
// but in that case we want to load the provider directly anyway so we can do this
// if the path is a .dll then we ask packagemanagement to load it for us
// it will also register the dll
PackageManagementService.LoadProviderAssembly(request, modulePath, true);
// now let's checked whether we can find it in the list of loaded providers
foreach (var loadedProvider in PackageManagementService.PackageProviders)
{
// the one loaded should have the same path
if (string.Equals(loadedProvider.ProviderPath, modulePath, StringComparison.OrdinalIgnoreCase))
{
return loadedProvider;
}
}
// if we reached here then we have failed to load the provider :(
return null;
}
}
string requiredVersionString = requiredVersion.ToString();
var provider = Create(request, modulePath, requiredVersionString, force, logWarning);
if (provider != null) {
var providerName = provider.GetPackageProviderName();
if (!string.IsNullOrWhiteSpace(providerName)) {
request.Debug(string.Format(CultureInfo.CurrentCulture, Resources.Messages.SuccessfullyLoadedModule, modulePath));
// looks good to me, let's add this to the list of moduels this meta provider can create.
var packageProvider = new PackageProvider(provider.As<IPackageProvider>()) {
IsLoaded = true,
Version = provider.GetProviderVersion(),
ProviderPath = modulePath
};
// take out powershell get
var psgetprovider = PackageManagementService.PackageProviders.FirstOrDefault(pv => string.Equals(pv.ProviderName, PowerShellGet, StringComparison.OrdinalIgnoreCase));
if (psModuleInfo != null)
{
// add swidtag information using moduleinfo
// however, this won't give us as much information yet
// we may have to fill this up later
ProvideSwidTagInformation(packageProvider, psModuleInfo);
}
AddToPackageProviderCacheTable(packageProvider);
_availableProviders.AddOrSet(providerName, provider);
return packageProvider;
} else {
provider.Dispose();
provider = null;
request.Debug(string.Format(CultureInfo.CurrentCulture, Resources.Messages.ProviderNameIsNullOrEmpty, modulePath));
}
}
return null;
}
/// <summary>
/// If we cannot use psget to get swidtag information, we will try to fill in some of the information
/// </summary>
/// <param name="packageProvider"></param>
/// <param name="psModuleInfo"></param>
private void ProvideSwidTagInformation(PackageProvider packageProvider, PSModuleInfo psModuleInfo)
{
if (packageProvider == null || psModuleInfo == null)
{
return;
}
packageProvider.VersionScheme = "MultiPartNumeric";
Microsoft.PackageManagement.Internal.Packaging.SoftwareMetadata softwareMetadata = new Microsoft.PackageManagement.Internal.Packaging.SoftwareMetadata();
bool changed = false;
var type = psModuleInfo.GetType();
// introduced in ps 2.0
if (!string.IsNullOrWhiteSpace(psModuleInfo.Description))
{
softwareMetadata.Description = psModuleInfo.Description;
changed = true;
}
// introduced in ps 3.0
if (!string.IsNullOrWhiteSpace(psModuleInfo.Copyright))
{
softwareMetadata.AddAttribute("copyright", psModuleInfo.Copyright);
changed = true;
}
// tags is introduced in ps 5.0
var tagsProperty = type.GetProperty("Tags");
bool isV5 = tagsProperty != null;
if (isV5)
{
// introduced in ps 5.0
var tags = tagsProperty.GetValue(psModuleInfo);
// check that we have something in tags
if (tags is IEnumerable<string> && (tags as IEnumerable<string>).Any())
{
softwareMetadata.AddAttribute("tags", string.Join(" ", (tags as IEnumerable<string>).Distinct()));
changed = true;
}
var releaseNotes = type.GetProperty("ReleaseNotes").GetValue(psModuleInfo);
// check that we have something in releasenotes
if (releaseNotes is string && !string.IsNullOrWhiteSpace(type.GetProperty("ReleaseNotes").GetValue(psModuleInfo) as string))
{
softwareMetadata.AddAttribute("tags", string.Join(" ", (tags as IEnumerable<string>).Distinct()));
changed = true;
}
}
if (changed)
{
packageProvider.AddElement(softwareMetadata);
}
if (isV5)
{
var iconUri = type.GetProperty("IconUri").GetValue(psModuleInfo);
// introduced in ps 5.0
if (iconUri is Uri)
{
packageProvider.AddLink(iconUri as Uri, "icon");
}
var licenseUri = type.GetProperty("LicenseUri").GetValue(psModuleInfo);
// introduced in ps 5.0
if (licenseUri is Uri)
{
packageProvider.AddLink(licenseUri as Uri, "license");
}
var projectUri = type.GetProperty("ProjectUri").GetValue(psModuleInfo);
// introduced in ps 5.0
if (projectUri is Uri)
{
packageProvider.AddLink(projectUri as Uri, "project");
}
}
// introduced in ps 3.0
if (!string.IsNullOrWhiteSpace(psModuleInfo.Author))
{
packageProvider.AddEntity(psModuleInfo.Author, null, "author");
}
// introduced in ps 3.0
if (!string.IsNullOrWhiteSpace(psModuleInfo.CompanyName))
{
packageProvider.AddEntity(psModuleInfo.CompanyName, null, "owner");
}
}
}
}