diff --git a/doBuild.ps1 b/doBuild.ps1
index 8ce91adc1..c4c0983a8 100644
--- a/doBuild.ps1
+++ b/doBuild.ps1
@@ -91,6 +91,7 @@ function DoBuild
'NuGet.Repositories'
'NuGet.Versioning'
'Newtonsoft.Json'
+ 'System.Text.Json'
)
$buildSuccess = $true
diff --git a/src/PSGet.Format.ps1xml b/src/PSGet.Format.ps1xml
index 5a7f14e81..9c9eb7b40 100644
--- a/src/PSGet.Format.ps1xml
+++ b/src/PSGet.Format.ps1xml
@@ -27,6 +27,28 @@
+
+ PSCommandResourceInfo
+
+ Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCommandResourceInfo
+
+
+
+
+
+
+
+
+
+
+ Names
+ $_.ParentResource.Name
+ $_.ParentResource.Version
+
+
+
+
+
PSIncludedResourceInfoTable
diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs
index 97457d74d..0e0c3a8f2 100644
--- a/src/code/FindHelper.cs
+++ b/src/code/FindHelper.cs
@@ -15,6 +15,7 @@
using System.Management.Automation;
using System.Net;
using System.Net.Http;
+using System.Runtime.ExceptionServices;
using System.Threading;
namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
@@ -29,20 +30,20 @@ internal class FindHelper
private CancellationToken _cancellationToken;
private readonly PSCmdlet _cmdletPassedIn;
private List _pkgsLeftToFind;
+ private List _tagsLeftToFind;
private ResourceType _type;
private string _version;
+ private VersionRange _versionRange;
+ private NuGetVersion _nugetVersion;
+ private VersionType _versionType;
private SwitchParameter _prerelease = false;
- private PSCredential _credential;
private string[] _tag;
private SwitchParameter _includeDependencies = false;
private readonly string _psGalleryRepoName = "PSGallery";
private readonly string _psGalleryScriptsRepoName = "PSGalleryScripts";
- private readonly string _psGalleryUri = "https://www.powershellgallery.com/api/v2";
private readonly string _poshTestGalleryRepoName = "PoshTestGallery";
- private readonly string _poshTestGalleryScriptsRepoName = "PoshTestGalleryScripts";
- private readonly string _poshTestGalleryUri = "https://www.poshtestgallery.com/api/v2";
- private bool _isADOFeedRepository;
private bool _repositoryNameContainsWildcard;
+ private NetworkCredential _networkCredential;
// NuGet's SearchAsync() API takes a top parameter of 6000, but testing shows for PSGallery
// usually a max of around 5990 is returned while more are left to retrieve in a second SearchAsync() call
@@ -56,46 +57,50 @@ internal class FindHelper
private FindHelper() { }
- public FindHelper(CancellationToken cancellationToken, PSCmdlet cmdletPassedIn)
+ public FindHelper(CancellationToken cancellationToken, PSCmdlet cmdletPassedIn, NetworkCredential networkCredential)
{
_cancellationToken = cancellationToken;
_cmdletPassedIn = cmdletPassedIn;
+ _networkCredential = networkCredential;
}
#endregion
- #region Public methods
+ #region Public Methods
- public List FindByResourceName(
+ public IEnumerable FindByResourceName(
string[] name,
ResourceType type,
+ VersionRange versionRange,
+ NuGetVersion nugetVersion,
+ VersionType versionType,
string version,
SwitchParameter prerelease,
string[] tag,
string[] repository,
- PSCredential credential,
SwitchParameter includeDependencies)
{
_type = type;
_version = version;
_prerelease = prerelease;
- _tag = tag;
- _credential = credential;
+ _tag = tag ?? Utils.EmptyStrArray;
_includeDependencies = includeDependencies;
-
- List foundPackages = new List();
+ _versionRange = versionRange;
+ _nugetVersion = nugetVersion;
+ _versionType = versionType;
if (name.Length == 0)
{
- return foundPackages;
+ yield break;
}
- _pkgsLeftToFind = name.ToList();
+ _pkgsLeftToFind = new List(name);
+ _tagsLeftToFind = tag == null ? new List() : new List(tag);
// Error out if repository array of names to be searched contains wildcards.
if (repository != null)
{
- repository = Utils.ProcessNameWildcards(repository, out string[] errorMsgs, out _repositoryNameContainsWildcard);
+ repository = Utils.ProcessNameWildcards(repository, removeWildcardEntries:false, out string[] errorMsgs, out _repositoryNameContainsWildcard);
foreach (string error in errorMsgs)
{
_cmdletPassedIn.WriteError(new ErrorRecord(
@@ -111,6 +116,15 @@ public List FindByResourceName(
try
{
repositoriesToSearch = RepositorySettings.Read(repository, out string[] errorList);
+ if (repository != null && repositoriesToSearch.Count == 0)
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException ("Cannot resolve -Repository name. Run 'Get-PSResourceRepository' to view all registered repositories."),
+ "RepositoryNameIsNotResolved",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+
foreach (string error in errorList)
{
_cmdletPassedIn.WriteError(new ErrorRecord(
@@ -128,177 +142,716 @@ public List FindByResourceName(
ErrorCategory.InvalidArgument,
this));
- return foundPackages;
+ yield break;
}
- // Loop through repositoriesToSearch and if PSGallery or PoshTestGallery add its Scripts endpoint repo
- // to list with same priority as PSGallery repo.
- // This special casing is done to handle PSGallery and PoshTestGallery having 2 endpoints currently for different resources.
- for (int i = 0; i < repositoriesToSearch.Count; i++)
+ for (int i = 0; i < repositoriesToSearch.Count && _pkgsLeftToFind.Any(); i++)
{
- if (String.Equals(repositoriesToSearch[i].Uri.AbsoluteUri, _psGalleryUri, StringComparison.InvariantCultureIgnoreCase))
+ if (repositoriesToSearch[i].ApiVersion == PSRepositoryInfo.APIVersion.local)
{
- // special case: for PowerShellGallery, Module and Script resources have different endpoints so separate repositories have to be registered
- // with those endpoints in order for the NuGet APIs to search across both in the case where name includes '*'
-
- // detect if Script repository needs to be added and/or Module repository needs to be skipped
- Uri psGalleryScriptsUri = new Uri("http://www.powershellgallery.com/api/v2/items/psscript/");
- PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUri, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null);
- if (_type == ResourceType.None)
- {
- _cmdletPassedIn.WriteVerbose("Null Type provided, so add PSGalleryScripts repository");
- repositoriesToSearch.Insert(i + 1, psGalleryScripts);
- }
- else if (_type != ResourceType.None && _type == ResourceType.Script)
+ foreach (PSResourceInfo currentPkg in SearchFromLocalRepository(repositoriesToSearch[i]))
{
- _cmdletPassedIn.WriteVerbose("Type Script provided, so add PSGalleryScripts and remove PSGallery (Modules only) from search consideration");
- repositoriesToSearch.Insert(i + 1, psGalleryScripts);
- repositoriesToSearch.RemoveAt(i); // remove PSGallery
+ yield return currentPkg;
}
}
- else if (String.Equals(repositoriesToSearch[i].Uri.AbsoluteUri, _poshTestGalleryUri, StringComparison.InvariantCultureIgnoreCase))
+ else
{
- // special case: for PoshTestGallery, Module and Script resources have different endpoints so separate repositories have to be registered
- // with those endpoints in order for the NuGet APIs to search across both in the case where name includes '*'
+ PSRepositoryInfo currentRepository = repositoriesToSearch[i];
- // detect if Script repository needs to be added and/or Module repository needs to be skipped
- Uri poshTestGalleryScriptsUri = new Uri("https://www.poshtestgallery.com/api/v2/items/psscript/");
- PSRepositoryInfo poshTestGalleryScripts = new PSRepositoryInfo(_poshTestGalleryScriptsRepoName, poshTestGalleryScriptsUri, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null);
- if (_type == ResourceType.None)
+ // Explicitly passed in Credential takes precedence over repository CredentialInfo.
+ if (_networkCredential == null && currentRepository.CredentialInfo != null)
{
- _cmdletPassedIn.WriteVerbose("Null Type provided, so add PoshTestGalleryScripts repository");
- repositoriesToSearch.Insert(i + 1, poshTestGalleryScripts);
+ PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
+ currentRepository.Name,
+ currentRepository.CredentialInfo,
+ _cmdletPassedIn);
+
+ var username = repoCredential.UserName;
+ var password = repoCredential.Password;
+
+ _networkCredential = new NetworkCredential(username, password);
+
+ _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + currentRepository.Name);
}
- else if (_type != ResourceType.None && _type == ResourceType.Script)
+
+ ServerApiCall currentServer = ServerFactory.GetServer(currentRepository, _networkCredential);
+ ResponseUtil currentResponseUtil = ResponseUtilFactory.GetResponseUtil(currentRepository);
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Searching in repository {0}", repositoriesToSearch[i].Name));
+
+
+ foreach (PSResourceInfo currentPkg in SearchByNames(currentServer, currentResponseUtil, currentRepository))
{
- _cmdletPassedIn.WriteVerbose("Type Script provided, so add PoshTestGalleryScripts and remove PoshTestGallery (Modules only) from search consideration");
- repositoriesToSearch.Insert(i + 1, poshTestGalleryScripts);
- repositoriesToSearch.RemoveAt(i); // remove PoshTestGallery
+ yield return currentPkg;
}
}
+ }
+ }
+ public IEnumerable FindCommandOrDscResource(
+ bool isSearchingForCommands,
+ SwitchParameter prerelease,
+ string[] tag,
+ string[] repository)
+ {
+ _prerelease = prerelease;
+
+ List cmdsLeftToFind = new List(tag);
+
+ if (tag.Length == 0)
+ {
+ yield break;
}
- for (int i = 0; i < repositoriesToSearch.Count && _pkgsLeftToFind.Any(); i++)
+ // Error out if repository array of names to be searched contains wildcards.
+ if (repository != null)
{
- _cmdletPassedIn.WriteVerbose(string.Format("Searching in repository {0}", repositoriesToSearch[i].Name));
- foreach (var pkg in SearchFromRepository(
- repositoryName: repositoriesToSearch[i].Name,
- repositoryUri: repositoriesToSearch[i].Uri,
- repositoryCredentialInfo: repositoriesToSearch[i].CredentialInfo))
+ repository = Utils.ProcessNameWildcards(repository, removeWildcardEntries:false, out string[] errorMsgs, out _repositoryNameContainsWildcard);
+
+ if (string.Equals(repository[0], "*"))
{
- foundPackages.Add(pkg);
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException ("-Repository parameter does not support entry '*' with -CommandName and -DSCResourceName parameters."),
+ "RepositoryDoesNotSupportWildcardEntryWithCmdOrDSCName",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+
+ foreach (string error in errorMsgs)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(
+ new PSInvalidOperationException(error),
+ "ErrorFilteringNamesForUnsupportedWildcards",
+ ErrorCategory.InvalidArgument,
+ this));
}
}
- return foundPackages;
- }
+ // Get repositories to search.
+ List repositoriesToSearch;
+ try
+ {
+ repositoriesToSearch = RepositorySettings.Read(repository, out string[] errorList);
+ if (repository != null && repositoriesToSearch.Count == 0)
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException ("Cannot resolve -Repository name. Run 'Get-PSResourceRepository' to view all registered repositories."),
+ "RepositoryNameIsNotResolved",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
- #endregion
+ foreach (string error in errorList)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(
+ new PSInvalidOperationException(error),
+ "ErrorGettingSpecifiedRepo",
+ ErrorCategory.InvalidOperation,
+ this));
+ }
+ }
+ catch (Exception e)
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSInvalidOperationException(e.Message),
+ "ErrorLoadingRepositoryStoreFile",
+ ErrorCategory.InvalidArgument,
+ this));
+
+ yield break;
+ }
- #region Private methods
+ for (int i = 0; i < repositoriesToSearch.Count && cmdsLeftToFind.Any(); i++)
+ {
+ PSRepositoryInfo currentRepository = repositoriesToSearch[i];
+
+ if (repositoriesToSearch[i].ApiVersion == PSRepositoryInfo.APIVersion.local)
+ {
+ _pkgsLeftToFind = new List() { "*" };
- private IEnumerable SearchFromRepository(
- string repositoryName,
- Uri repositoryUri,
- PSCredentialInfo repositoryCredentialInfo)
+ var potentialMatches = SearchFromLocalRepository(repositoriesToSearch[i]);
+ foreach (string cmdOrDsc in tag)
+ {
+ foreach (var package in potentialMatches)
+ {
+ // this check ensures DSC names provided as a Command name won't get returned mistakenly
+ // -CommandName "command1", "dsc1" <- (will not return or add DSC name)
+ if ((isSearchingForCommands && package.Includes.Command.Contains(cmdOrDsc, StringComparer.OrdinalIgnoreCase)) ||
+ (!isSearchingForCommands && package.Includes.DscResource.Contains(cmdOrDsc, StringComparer.OrdinalIgnoreCase)))
+ {
+ yield return new PSCommandResourceInfo(new string[] { cmdOrDsc }, package);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Explicitly passed in Credential takes precedence over repository CredentialInfo.
+ if (_networkCredential == null && currentRepository.CredentialInfo != null)
+ {
+ PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
+ currentRepository.Name,
+ currentRepository.CredentialInfo,
+ _cmdletPassedIn);
+
+ var username = repoCredential.UserName;
+ var password = repoCredential.Password;
+
+ _networkCredential = new NetworkCredential(username, password);
+
+ _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + currentRepository.Name);
+ }
+
+ ServerApiCall currentServer = ServerFactory.GetServer(currentRepository, _networkCredential);
+ ResponseUtil currentResponseUtil = ResponseUtilFactory.GetResponseUtil(currentRepository);
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Searching in repository {0}", repositoriesToSearch[i].Name));
+
+ foreach (string currentCmdOrDSC in tag)
+ {
+ string[] responses = currentServer.FindCommandOrDscResource(currentCmdOrDSC, _prerelease, isSearchingForCommands, out ExceptionDispatchInfo edi);
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindCommandOrDSCResourceFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(currentResult.errorMsg), "FindCmdOrDSCResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ cmdsLeftToFind.Remove(currentCmdOrDSC);
+ PSCommandResourceInfo currentCmdPkg = new PSCommandResourceInfo(new string[] { currentCmdOrDSC }, currentResult.returnedObject);
+ yield return currentCmdPkg;
+ }
+ }
+ }
+ }
+ }
+
+ public IEnumerable FindTag(
+ ResourceType type,
+ SwitchParameter prerelease,
+ string[] tag,
+ string[] repository)
{
- PackageSearchResource resourceSearch;
- PackageMetadataResource resourceMetadata;
- SearchFilter filter;
- SourceCacheContext context;
+ _type = type;
+ _prerelease = prerelease;
+ _tag = tag;
- // File based Uri scheme.
- if (repositoryUri.Scheme == Uri.UriSchemeFile)
+ _tagsLeftToFind = new List(tag);
+
+ if (tag.Length == 0)
{
- FindLocalPackagesResourceV2 localResource = new FindLocalPackagesResourceV2(repositoryUri.ToString());
- resourceSearch = new LocalPackageSearchResource(localResource);
- resourceMetadata = new LocalPackageMetadataResource(localResource);
- filter = new SearchFilter(_prerelease);
- context = new SourceCacheContext();
+ yield break;
+ }
- foreach(PSResourceInfo pkg in SearchAcrossNamesInRepository(
- repositoryName: repositoryName,
- pkgSearchResource: resourceSearch,
- pkgMetadataResource: resourceMetadata,
- searchFilter: filter,
- sourceContext: context))
+ if (repository != null)
+ {
+ repository = Utils.ProcessNameWildcards(repository, removeWildcardEntries:false, out string[] errorMsgs, out _repositoryNameContainsWildcard);
+
+ if (string.Equals(repository[0], "*"))
{
- yield return pkg;
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException ("-Repository parameter does not support entry '*' with -Tag parameter."),
+ "RepositoryDoesNotSupportWildcardEntryWithTag",
+ ErrorCategory.InvalidArgument,
+ this));
}
+
+ foreach (string error in errorMsgs)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(
+ new PSInvalidOperationException(error),
+ "ErrorFilteringNamesForUnsupportedWildcards",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+ }
+
+ // Get repositories to search.
+ List repositoriesToSearch;
+ try
+ {
+ repositoriesToSearch = RepositorySettings.Read(repository, out string[] errorList);
+ if (repository != null && repositoriesToSearch.Count == 0)
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException ("Cannot resolve -Repository name. Run 'Get-PSResourceRepository' to view all registered repositories."),
+ "RepositoryNameIsNotResolved",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+
+ foreach (string error in errorList)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(
+ new PSInvalidOperationException(error),
+ "ErrorGettingSpecifiedRepo",
+ ErrorCategory.InvalidOperation,
+ this));
+ }
+ }
+ catch (Exception e)
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSInvalidOperationException(e.Message),
+ "ErrorLoadingRepositoryStoreFile",
+ ErrorCategory.InvalidArgument,
+ this));
+
yield break;
}
- // Check if ADOFeed- for which searching for Name with wildcard has a different logic flow.
- if (repositoryUri.ToString().Contains("pkgs."))
+ for (int i = 0; i < repositoriesToSearch.Count && _tagsLeftToFind.Any(); i++)
{
- _isADOFeedRepository = true;
+ PSRepositoryInfo currentRepository = repositoriesToSearch[i];
+
+ if (repositoriesToSearch[i].ApiVersion == PSRepositoryInfo.APIVersion.local)
+ {
+ _pkgsLeftToFind = new List() { "*" };
+ _tag = tag;
+
+ foreach (var package in SearchFromLocalRepository(repositoriesToSearch[i]))
+ {
+ yield return package;
+ }
+ }
+ else
+ {
+ // Explicitly passed in Credential takes precedence over repository CredentialInfo.
+ if (_networkCredential == null && currentRepository.CredentialInfo != null)
+ {
+ PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
+ currentRepository.Name,
+ currentRepository.CredentialInfo,
+ _cmdletPassedIn);
+
+ var username = repoCredential.UserName;
+ var password = repoCredential.Password;
+
+ _networkCredential = new NetworkCredential(username, password);
+
+ _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + currentRepository.Name);
+ }
+
+
+ ServerApiCall currentServer = ServerFactory.GetServer(currentRepository, _networkCredential);
+ ResponseUtil currentResponseUtil = ResponseUtilFactory.GetResponseUtil(currentRepository);
+
+ if (_type != ResourceType.None && repositoriesToSearch[i].Name != "PSGallery")
+ {
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new PSInvalidOperationException("-Type parameter is only supported with the PowerShellGallery."),
+ "ErrorUsingTypeParameter",
+ ErrorCategory.InvalidOperation,
+ this));
+ }
+
+ foreach (string currentTag in _tag)
+ {
+ string[] responses = currentServer.FindTag(currentTag, _prerelease, type, out ExceptionDispatchInfo edi);
+
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindTagFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Tags: {String.Join(", ", _tag)} could not be found due to: {currentResult.errorMsg}";
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(errMsg), "FindTagResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ yield return currentResult.returnedObject;
+ }
+ }
+ }
}
+ }
+
+ #endregion
+
+ #region Private HTTP Client Search Methods
- // HTTP, HTTPS, FTP Uri schemes (only other Uri schemes allowed by RepositorySettings.Read() API).
- PackageSource source = new PackageSource(repositoryUri.ToString());
+ private IEnumerable SearchByNames(ServerApiCall currentServer, ResponseUtil currentResponseUtil, PSRepositoryInfo repository)
+ {
+ ExceptionDispatchInfo edi = null;
+ List parentPkgs = new List();
+ HashSet pkgsFound = new HashSet(StringComparer.OrdinalIgnoreCase);
- // Explicitly passed in Credential takes precedence over repository CredentialInfo.
- if (_credential != null)
+ foreach (string pkgName in _pkgsLeftToFind.ToArray())
{
- string password = new NetworkCredential(string.Empty, _credential.Password).Password;
- source.Credentials = PackageSourceCredential.FromUserInput(repositoryUri.ToString(), _credential.UserName, password, true, null);
- _cmdletPassedIn.WriteVerbose("credential successfully set for repository: " + repositoryName);
+ if (_versionType == VersionType.NoVersion)
+ {
+ if (pkgName.Trim().Equals("*"))
+ {
+ // Example: Find-PSResource -Name "*"
+ string[] responses = currentServer.FindAll(_prerelease, _type, out edi);
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindAllFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Package with search criteria: Name {pkgName} could not be found due to: {currentResult.errorMsg}.";
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(errMsg), "FindAllResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ PSResourceInfo foundPkg = currentResult.returnedObject;
+ parentPkgs.Add(foundPkg);
+ pkgsFound.Add(Utils.CreateHashSetKey(foundPkg.Name, foundPkg.Version.ToString()));
+ yield return foundPkg;
+ }
+ }
+ else if(pkgName.Contains("*"))
+ {
+ // Example: Find-PSResource -Name "Az*"
+ // Example: Find-PSResource -Name "Az*" -Tag "Storage"
+ string tagMsg = String.Empty;
+ string[] responses = Utils.EmptyStrArray;
+ if (_tag.Length == 0)
+ {
+ responses = currentServer.FindNameGlobbing(pkgName, _prerelease, _type, out edi);
+ }
+ else
+ {
+ responses = currentServer.FindNameGlobbingWithTag(pkgName, _tag, _prerelease, _type, out edi);
+ string tagsAsString = String.Join(", ", _tag);
+ tagMsg = $" and Tags {tagsAsString}";
+ }
+
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindNameGlobbingFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Package with search criteria: Name {pkgName}{tagMsg} could not be found due to: {currentResult.errorMsg} originating at method: FindNameGlobbingResponseConversionFail().";
+ _cmdletPassedIn.WriteWarning(errMsg);
+ continue;
+ }
+
+ PSResourceInfo foundPkg = currentResult.returnedObject;
+ parentPkgs.Add(foundPkg);
+ pkgsFound.Add(Utils.CreateHashSetKey(foundPkg.Name, foundPkg.Version.ToString()));
+ yield return foundPkg;
+ }
+ }
+ else
+ {
+ // Example: Find-PSResource -Name "Az"
+ // Example: Find-PSResource -Name "Az" -Tag "Storage"
+ string tagMsg = String.Empty;
+ string response = String.Empty;
+ if (_tag.Length == 0)
+ {
+ response = currentServer.FindName(pkgName, _prerelease, _type, out edi);
+ }
+ else
+ {
+ response = currentServer.FindNameWithTag(pkgName, _tag, _prerelease, _type, out edi);
+ string tagsAsString = String.Join(", ", _tag);
+ tagMsg = $" and Tags {tagsAsString}";
+ }
+
+ string[] responses = new string[]{ response };
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindNameFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses: responses).First();
+
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Package with search criteria: Name {pkgName}{tagMsg} could not be found due to: {currentResult.errorMsg}.";
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(errMsg), "FindNameResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ PSResourceInfo foundPkg = currentResult.returnedObject;
+ parentPkgs.Add(foundPkg);
+ pkgsFound.Add(Utils.CreateHashSetKey(foundPkg.Name, foundPkg.Version.ToString()));
+ yield return foundPkg;
+ }
+ }
+ else if (_versionType == VersionType.SpecificVersion)
+ {
+ if (pkgName.Contains("*"))
+ {
+ var exMessage = "Name cannot contain or equal wildcard when using specific version.";
+ var ex = new ArgumentException(exMessage);
+ var WildcardError = new ErrorRecord(ex, "InvalidWildCardUsage", ErrorCategory.InvalidOperation, null);
+ _cmdletPassedIn.WriteError(WildcardError);
+
+ continue;
+ }
+ else
+ {
+ // Example: Find-PSResource -Name "Az" -Version "3.0.0.0"
+ // Example: Find-PSResource -Name "Az" -Version "3.0.0.0" -Tag "Windows"
+ string response = String.Empty;
+ string tagMsg = String.Empty;
+ if (_tag.Length == 0)
+ {
+ response = currentServer.FindVersion(pkgName, _nugetVersion.ToNormalizedString(), _type, out edi);
+ }
+ else
+ {
+ response = currentServer.FindVersionWithTag(pkgName, _nugetVersion.ToNormalizedString(), _tag, _type, out edi);
+ string tagsAsString = String.Join(", ", _tag);
+ tagMsg = $" and Tags {tagsAsString}";
+ }
+
+ string[] responses = new string[]{ response };
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindVersionFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses: responses).First();
+
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Package with search criteria: Name {pkgName}, Version {_version} {tagMsg} could not be found due to: {currentResult.errorMsg}.";
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(errMsg), "FindVersionResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ PSResourceInfo foundPkg = currentResult.returnedObject;
+ parentPkgs.Add(foundPkg);
+ pkgsFound.Add(Utils.CreateHashSetKey(foundPkg.Name, foundPkg.Version.ToString()));
+ yield return foundPkg;
+ }
+ }
+ else
+ {
+ // version type is Version Range
+ if (pkgName.Contains("*"))
+ {
+ var exMessage = "Name cannot contain or equal wildcard when using version range";
+ var ex = new ArgumentException(exMessage);
+ var WildcardError = new ErrorRecord(ex, "InvalidWildCardUsage", ErrorCategory.InvalidOperation, null);
+ _cmdletPassedIn.WriteError(WildcardError);
+ }
+ else
+ {
+ // Example: Find-PSResource -Name "Az" -Version "[1.0.0.0, 3.0.0.0]"
+ string[] responses = Utils.EmptyStrArray;
+ if (_tag.Length == 0)
+ {
+ responses = currentServer.FindVersionGlobbing(pkgName, _versionRange, _prerelease, _type, getOnlyLatest: false, out edi);
+ }
+ else
+ {
+ var exMessage = "Name cannot contain or equal wildcard when using version range";
+ var ex = new ArgumentException(exMessage);
+ var WildcardError = new ErrorRecord(ex, "InvalidWildCardUsage", ErrorCategory.InvalidOperation, null);
+ _cmdletPassedIn.WriteError(WildcardError);
+ continue;
+ }
+
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "FindVersionGlobbingFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ string errMsg = $"Package with search criteria: Name {pkgName} and Version {_version} could not be found due to: {currentResult.errorMsg} originating at method FindVersionGlobbingResponseConversionFail().";
+ _cmdletPassedIn.WriteWarning(errMsg);
+ continue;
+ }
+
+ PSResourceInfo foundPkg = currentResult.returnedObject;
+ parentPkgs.Add(foundPkg);
+ pkgsFound.Add(Utils.CreateHashSetKey(foundPkg.Name, foundPkg.Version.ToString()));
+ yield return foundPkg;
+ }
+ }
+ }
}
- else if (repositoryCredentialInfo != null)
+
+ // After retrieving all packages find their dependencies
+ if (_includeDependencies)
{
- PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
- repositoryName,
- repositoryCredentialInfo,
- _cmdletPassedIn);
-
- string password = new NetworkCredential(string.Empty, repoCredential.Password).Password;
- source.Credentials = PackageSourceCredential.FromUserInput(repositoryUri.ToString(), repoCredential.UserName, password, true, null);
- _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName);
+ if (currentServer.repository.ApiVersion == PSRepositoryInfo.APIVersion.v3)
+ {
+ _cmdletPassedIn.WriteWarning("Installing dependencies is not currently supported for V3 server protocol repositories. The package will be installed without installing dependencies.");
+ yield break;
+ }
+
+ foreach (PSResourceInfo currentPkg in parentPkgs)
+ {
+ foreach (PSResourceInfo pkgDep in HttpFindDependencyPackages(currentServer, currentResponseUtil, currentPkg, repository, pkgsFound))
+ {
+ yield return pkgDep;
+ }
+ }
}
+ }
- // GetCoreV3() API is able to handle V2 and V3 repository endpoints.
- var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider);
- SourceRepository repository = new SourceRepository(source, provider);
- resourceSearch = null;
- resourceMetadata = null;
+ private bool IsTagMatch(PSResourceInfo pkg)
+ {
+ List matchedTags = _tag.Intersect(pkg.Tags, StringComparer.InvariantCultureIgnoreCase).ToList();
- try
+ foreach (string tag in matchedTags)
{
- resourceSearch = repository.GetResourceAsync().GetAwaiter().GetResult();
- resourceMetadata = repository.GetResourceAsync().GetAwaiter().GetResult();
+ _tagsLeftToFind.Remove(tag);
}
- catch (Exception e)
+
+ return matchedTags.Count > 0;
+ }
+
+ #endregion
+
+ #region Internal HTTP Client Search Methods
+ internal IEnumerable HttpFindDependencyPackages(
+ ServerApiCall currentServer,
+ ResponseUtil currentResponseUtil,
+ PSResourceInfo currentPkg,
+ PSRepositoryInfo repository,
+ HashSet foundPkgs)
+ {
+ if (currentPkg.Dependencies.Length > 0)
{
- Utils.WriteVerboseOnCmdlet(_cmdletPassedIn, "Error retrieving resource from repository: " + e.Message);
+ foreach (var dep in currentPkg.Dependencies)
+ {
+ PSResourceInfo depPkg = null;
+
+ if (dep.VersionRange == VersionRange.All)
+ {
+ string response = currentServer.FindName(dep.Name, _prerelease, _type, out ExceptionDispatchInfo edi);
+ string[] responses = new string[] { response };
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "HttpFindDepPackagesFindNameFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses: responses).First();
+
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(currentResult.errorMsg), "FindNameForDepResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ depPkg = currentResult.returnedObject;
+ string pkgHashKey = Utils.CreateHashSetKey(depPkg.Name, depPkg.Version.ToString());
+
+ if (!foundPkgs.Contains(pkgHashKey))
+ {
+ foreach (PSResourceInfo depRes in HttpFindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository, foundPkgs))
+ {
+ yield return depRes;
+ }
+ }
+ }
+ else
+ {
+ string[] responses = currentServer.FindVersionGlobbing(dep.Name, dep.VersionRange, _prerelease, ResourceType.None, getOnlyLatest: true, out ExceptionDispatchInfo edi);
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "HttpFindDepPackagesFindVersionGlobbingFail", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ foreach (PSResourceResult currentResult in currentResponseUtil.ConvertToPSResourceResult(responses: responses))
+ {
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(new PSInvalidOperationException(currentResult.errorMsg), "FindVersionGlobbingForDepResponseConversionFail", ErrorCategory.NotSpecified, this));
+ continue;
+ }
+
+ depPkg = currentResult.returnedObject;
+ }
+
+ string pkgHashKey = Utils.CreateHashSetKey(depPkg.Name, depPkg.Version.ToString());
+
+ if (!foundPkgs.Contains(pkgHashKey))
+ {
+ foreach (PSResourceInfo depRes in HttpFindDependencyPackages(currentServer, currentResponseUtil, depPkg, repository, foundPkgs))
+ {
+ yield return depRes;
+ }
+ }
+ }
+ }
}
- if (resourceSearch == null || resourceMetadata == null)
+ string currentPkgHashKey = Utils.CreateHashSetKey(currentPkg.Name, currentPkg.Version.ToString());
+
+ if (!foundPkgs.Contains(currentPkgHashKey))
{
- yield break;
+ yield return currentPkg;
}
+ }
+
- filter = new SearchFilter(_prerelease);
- context = new SourceCacheContext();
+ #endregion
- foreach(PSResourceInfo pkg in SearchAcrossNamesInRepository(
- repositoryName: String.Equals(repositoryUri.AbsoluteUri, _psGalleryUri, StringComparison.InvariantCultureIgnoreCase) ? _psGalleryRepoName :
- (String.Equals(repositoryUri.AbsoluteUri, _poshTestGalleryUri, StringComparison.InvariantCultureIgnoreCase) ? _poshTestGalleryRepoName : repositoryName),
- pkgSearchResource: resourceSearch,
- pkgMetadataResource: resourceMetadata,
- searchFilter: filter,
- sourceContext: context))
+ #region Private NuGet APIs for Local Repo
+
+ private IEnumerable SearchFromLocalRepository(PSRepositoryInfo repositoryInfo)
+ {
+ PackageSearchResource resourceSearch;
+ PackageMetadataResource resourceMetadata;
+ SearchFilter filter;
+ SourceCacheContext context;
+
+ // File based Uri scheme.
+ if (repositoryInfo.Uri.Scheme == Uri.UriSchemeFile)
{
- yield return pkg;
+ FindLocalPackagesResourceV2 localResource = new FindLocalPackagesResourceV2(repositoryInfo.Uri.ToString());
+ resourceSearch = new LocalPackageSearchResource(localResource);
+ resourceMetadata = new LocalPackageMetadataResource(localResource);
+ filter = new SearchFilter(_prerelease);
+ context = new SourceCacheContext();
+
+ foreach (PSResourceInfo pkg in SearchAcrossNamesInRepository(
+ repositoryName: repositoryInfo.Name,
+ pkgSearchResource: resourceSearch,
+ pkgMetadataResource: resourceMetadata,
+ searchFilter: filter,
+ sourceContext: context))
+ {
+ yield return pkg;
+ }
+ yield break;
}
}
private IEnumerable SearchAcrossNamesInRepository(
- string repositoryName,
- PackageSearchResource pkgSearchResource,
- PackageMetadataResource pkgMetadataResource,
- SearchFilter searchFilter,
- SourceCacheContext sourceContext)
+ string repositoryName,
+ PackageSearchResource pkgSearchResource,
+ PackageMetadataResource pkgMetadataResource,
+ SearchFilter searchFilter,
+ SourceCacheContext sourceContext)
{
foreach (string pkgName in _pkgsLeftToFind.ToArray())
{
@@ -309,6 +862,7 @@ private IEnumerable SearchAcrossNamesInRepository(
continue;
}
+ // call NuGet client API
foreach (PSResourceInfo pkg in FindFromPackageSourceSearchAPI(
repositoryName: repositoryName,
pkgName: pkgName,
@@ -400,35 +954,26 @@ private IEnumerable FindFromPackageSourceSearchAPI(
}
else
{
- if (_isADOFeedRepository)
- {
- _cmdletPassedIn.WriteError(new ErrorRecord(
- new ArgumentException(String.Format("Searching through ADOFeed with wildcard in Name is not supported, so {0} repository will be skipped.", repositoryName)),
- "CannotSearchADOFeedWithWildcardName",
- ErrorCategory.InvalidArgument,
- this));
- yield break;
- }
-
// Case: searching for name containing wildcard i.e "Carbon.*".
List wildcardPkgs;
try
{
+ string wildcardPkgName = string.Empty;
// SearchAsync() API returns the latest version only for all packages that match the wild-card name
wildcardPkgs = pkgSearchResource.SearchAsync(
- searchTerm: pkgName,
+ searchTerm: wildcardPkgName,
filters: searchFilter,
skip: 0,
take: SearchAsyncMaxTake,
log: NullLogger.Instance,
cancellationToken: _cancellationToken).GetAwaiter().GetResult().ToList();
-
+
if (wildcardPkgs.Count > SearchAsyncMaxReturned)
{
// Get the rest of the packages.
wildcardPkgs.AddRange(
pkgSearchResource.SearchAsync(
- searchTerm: pkgName,
+ searchTerm: wildcardPkgName,
filters: searchFilter,
skip: SearchAsyncMaxTake,
take: GalleryMax,
@@ -552,7 +1097,7 @@ private IEnumerable FindFromPackageSourceSearchAPI(
repositoryName: repositoryName,
type: _type,
errorMsg: out string errorMsg))
- {
+ {
_cmdletPassedIn.WriteError(new ErrorRecord(
new PSInvalidOperationException("Error parsing IPackageSearchMetadata to PSResourceInfo with message: " + errorMsg),
"IPackageSearchMetadataToPSResourceInfoParsingError",
@@ -561,20 +1106,8 @@ private IEnumerable FindFromPackageSourceSearchAPI(
yield break;
}
- if (_type != ResourceType.None)
- {
- if (_type == ResourceType.Command && !currentPkg.Type.HasFlag(ResourceType.Command))
- {
- continue;
- }
- if (_type == ResourceType.DscResource && !currentPkg.Type.HasFlag(ResourceType.DscResource))
- {
- continue;
- }
- }
-
// Only going to go in here for the main package, resolve Type and Tag requirements if any, and then find dependencies
- if (_tag == null || (_tag != null && IsTagMatch(currentPkg)))
+ if (_tag == null || _tag.Length == 0 || (_tag != null && _tag.Length > 0 && IsTagMatch(currentPkg)))
{
yield return currentPkg;
@@ -589,16 +1122,10 @@ private IEnumerable FindFromPackageSourceSearchAPI(
}
}
- private bool IsTagMatch(PSResourceInfo pkg)
- {
- return _tag.Intersect(pkg.Tags, StringComparer.InvariantCultureIgnoreCase).ToList().Count > 0;
- }
-
private List FindDependencyPackages(
PSResourceInfo currentPkg,
PackageMetadataResource packageMetadataResource,
- SourceCacheContext sourceCacheContext
- )
+ SourceCacheContext sourceCacheContext)
{
List thoseToAdd = new List();
FindDependencyPackagesHelper(currentPkg, thoseToAdd, packageMetadataResource, sourceCacheContext);
@@ -609,10 +1136,9 @@ private void FindDependencyPackagesHelper(
PSResourceInfo currentPkg,
List thoseToAdd,
PackageMetadataResource packageMetadataResource,
- SourceCacheContext sourceCacheContext
- )
+ SourceCacheContext sourceCacheContext)
{
- foreach(var dep in currentPkg.Dependencies)
+ foreach (var dep in currentPkg.Dependencies)
{
List depPkgs = packageMetadataResource.GetMetadataAsync(
packageId: dep.Name,
@@ -682,7 +1208,7 @@ SourceCacheContext sourceCacheContext
}
}
}
-
+
#endregion
}
}
diff --git a/src/code/FindPSResource.cs b/src/code/FindPSResource.cs
index ceb839504..5e8c9c55d 100644
--- a/src/code/FindPSResource.cs
+++ b/src/code/FindPSResource.cs
@@ -2,10 +2,12 @@
// Licensed under the MIT License.
using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
+using System.Net;
using System.Threading;
using Dbg = System.Diagnostics.Debug;
@@ -20,13 +22,13 @@ namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
///
[Cmdlet(VerbsCommon.Find,
"PSResource",
- DefaultParameterSetName = ResourceNameParameterSet)]
+ DefaultParameterSetName = NameParameterSet)]
[OutputType(typeof(PSResourceInfo), typeof(PSCommandResourceInfo))]
public sealed class FindPSResource : PSCmdlet
{
#region Members
- private const string ResourceNameParameterSet = "ResourceNameParameterSet";
+ private const string NameParameterSet = "NameParameterSet";
private const string CommandNameParameterSet = "CommandNameParameterSet";
private const string DscResourceNameParameterSet = "DscResourceNameParameterSet";
private CancellationTokenSource _cancellationTokenSource;
@@ -42,46 +44,32 @@ public sealed class FindPSResource : PSCmdlet
[SupportsWildcards]
[Parameter(Position = 0,
ValueFromPipeline = true,
- ParameterSetName = ResourceNameParameterSet)]
+ ParameterSetName = NameParameterSet)]
[ValidateNotNullOrEmpty]
public string[] Name { get; set; }
///
/// Specifies one or more resource types to find.
- /// Resource types supported are: Module, Script, Command, DscResource
+ /// Resource types supported are: Module, Script
///
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
+ [Parameter(ParameterSetName = NameParameterSet)]
public ResourceType Type { get; set; }
///
/// Specifies the version of the resource to be found and returned. Wildcards are supported.
///
- [SupportsWildcards]
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter(ParameterSetName = NameParameterSet)]
[ValidateNotNullOrEmpty]
public string Version { get; set; }
///
/// When specified, includes prerelease versions in search.
///
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter()]
public SwitchParameter Prerelease { get; set; }
///
- /// Specifies a module resource package name type to search for. Wildcards are supported.
- ///
- [SupportsWildcards]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
- [ValidateNotNullOrEmpty]
- public string[] ModuleName { get; set; }
-
- ///
- /// Specifies a list of command names that searched module packages will provide.
+ /// Specifies a list of command names that searched module packages will provide. Wildcards are supported.
///
[Parameter(Mandatory = true, ParameterSetName = CommandNameParameterSet)]
[ValidateNotNullOrEmpty]
@@ -97,19 +85,14 @@ public sealed class FindPSResource : PSCmdlet
///
/// Filters search results for resources that include one or more of the specified tags.
///
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter(ParameterSetName = NameParameterSet)]
[ValidateNotNull]
public string[] Tag { get; set; }
///
/// Specifies one or more repository names to search. If not specified, search will include all currently registered repositories.
///
- [SupportsWildcards]
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter()]
[ArgumentCompleter(typeof(RepositoryNameCompleter))]
[ValidateNotNullOrEmpty]
public string[] Repository { get; set; }
@@ -117,29 +100,29 @@ public sealed class FindPSResource : PSCmdlet
///
/// Specifies optional credentials to be used when accessing a repository.
///
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter()]
public PSCredential Credential { get; set; }
///
/// When specified, search will return all matched resources along with any resources the matched resources depends on.
///
- [Parameter(ParameterSetName = ResourceNameParameterSet)]
- [Parameter(ParameterSetName = CommandNameParameterSet)]
- [Parameter(ParameterSetName = DscResourceNameParameterSet)]
+ [Parameter(ParameterSetName = NameParameterSet)]
public SwitchParameter IncludeDependencies { get; set; }
#endregion
- #region Method overrides
+ #region Method Overrides
protected override void BeginProcessing()
{
_cancellationTokenSource = new CancellationTokenSource();
+
+ var networkCred = Credential != null ? new NetworkCredential(Credential.UserName, Credential.Password) : null;
+
_findHelper = new FindHelper(
cancellationToken: _cancellationTokenSource.Token,
- cmdletPassedIn: this);
+ cmdletPassedIn: this,
+ networkCredential: networkCred);
// Create a repository story (the PSResourceRepository.xml file) if it does not already exist
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
@@ -161,7 +144,7 @@ protected override void ProcessRecord()
{
switch (ParameterSetName)
{
- case ResourceNameParameterSet:
+ case NameParameterSet:
ProcessResourceNameParameterSet();
break;
@@ -181,27 +164,34 @@ protected override void ProcessRecord()
#endregion
- #region Private methods
+ #region Private Methods
private void ProcessResourceNameParameterSet()
{
+ // only cases where Name is allowed to not be specified is if Type or Tag parameters are
if (!MyInvocation.BoundParameters.ContainsKey(nameof(Name)))
{
- // only cases where Name is allowed to not be specified is if Type or Tag parameters are
- if (!MyInvocation.BoundParameters.ContainsKey(nameof(Type)) && !MyInvocation.BoundParameters.ContainsKey(nameof(Tag)))
+ if (MyInvocation.BoundParameters.ContainsKey(nameof(Tag)))
+ {
+ ProcessTags();
+ return;
+ }
+ else if (MyInvocation.BoundParameters.ContainsKey(nameof(Type)))
+ {
+ Name = new string[] {"*"};
+ }
+ else
{
ThrowTerminatingError(
new ErrorRecord(
- new PSInvalidOperationException("Name parameter must be provided."),
+ new PSInvalidOperationException("Name parameter must be provided, unless Tag or Type parameters are used."),
"NameParameterNotProvided",
ErrorCategory.InvalidOperation,
this));
}
-
- Name = new string[] {"*"};
}
- Name = Utils.ProcessNameWildcards(Name, out string[] errorMsgs, out bool nameContainsWildcard);
+ Name = Utils.ProcessNameWildcards(Name, removeWildcardEntries:false, out string[] errorMsgs, out bool nameContainsWildcard);
foreach (string error in errorMsgs)
{
@@ -217,23 +207,55 @@ private void ProcessResourceNameParameterSet()
if (Name.Length == 0)
{
return;
- }
+ }
- List foundPackages = _findHelper.FindByResourceName(
+ // determine/parse out Version param
+ VersionType versionType = VersionType.VersionRange;
+ NuGetVersion nugetVersion = null;
+ VersionRange versionRange = null;
+
+ if (Version != null)
+ {
+ if (!NuGetVersion.TryParse(Version, out nugetVersion))
+ {
+ if (Version.Trim().Equals("*"))
+ {
+ versionRange = VersionRange.All;
+ versionType = VersionType.VersionRange;
+ }
+ else if (!VersionRange.TryParse(Version, out versionRange))
+ {
+ WriteError(new ErrorRecord(
+ new ArgumentException("Argument for -Version parameter is not in the proper format"),
+ "IncorrectVersionFormat",
+ ErrorCategory.InvalidArgument,
+ this));
+ return;
+ }
+ }
+ else
+ {
+ versionType = VersionType.SpecificVersion;
+ }
+ }
+ else
+ {
+ versionType = VersionType.NoVersion;
+ }
+
+ foreach (PSResourceInfo pkg in _findHelper.FindByResourceName(
name: Name,
type: Type,
+ versionRange: versionRange,
+ nugetVersion: nugetVersion,
+ versionType: versionType,
version: Version,
prerelease: Prerelease,
tag: Tag,
repository: Repository,
- credential: Credential,
- includeDependencies: IncludeDependencies);
-
- foreach (var uniquePackageVersion in foundPackages.GroupBy(
- m => new {m.Name, m.Version, m.Repository}).Select(
- group => group.First()))
+ includeDependencies: IncludeDependencies))
{
- WriteObject(uniquePackageVersion);
+ WriteObject(pkg);
}
}
@@ -241,24 +263,16 @@ private void ProcessCommandOrDscParameterSet(bool isSearchingForCommands)
{
var commandOrDSCNamesToSearch = Utils.ProcessNameWildcards(
pkgNames: isSearchingForCommands ? CommandName : DscResourceName,
+ removeWildcardEntries: true,
errorMsgs: out string[] errorMsgs,
- isContainWildcard: out bool nameContainsWildcard);
-
- if (nameContainsWildcard)
- {
- WriteError(new ErrorRecord(
- new PSInvalidOperationException("Wilcards are not supported for -CommandName or -DSCResourceName for Find-PSResource. So all CommandName or DSCResourceName entries will be discarded."),
- "CommandDSCResourceNameWithWildcardsNotSupported",
- ErrorCategory.InvalidArgument,
- this));
- return;
- }
+ isContainWildcard: out bool _);
+ var paramName = isSearchingForCommands ? "CommandName" : "DscResourceName";
foreach (string error in errorMsgs)
{
WriteError(new ErrorRecord(
- new PSInvalidOperationException(error),
- "ErrorFilteringCommandDscResourceNamesForUnsupportedWildcards",
+ new PSInvalidOperationException($"Wildcards are not supported for -{paramName}: {error}"),
+ "WildcardsUnsupportedForCommandNameorDSCResourceName",
ErrorCategory.InvalidArgument,
this));
}
@@ -270,50 +284,47 @@ private void ProcessCommandOrDscParameterSet(bool isSearchingForCommands)
return;
}
- var moduleNamesToSearch = Utils.ProcessNameWildcards(
- pkgNames: ModuleName,
- errorMsgs: out string[] moduleErrorMsgs,
+ foreach (PSCommandResourceInfo cmdPkg in _findHelper.FindCommandOrDscResource(
+ isSearchingForCommands: isSearchingForCommands,
+ prerelease: Prerelease,
+ tag: commandOrDSCNamesToSearch,
+ repository: Repository))
+ {
+ WriteObject(cmdPkg);
+ }
+ }
+
+ private void ProcessTags()
+ {
+ var tagsToSearch = Utils.ProcessNameWildcards(
+ pkgNames: Tag,
+ removeWildcardEntries: true,
+ errorMsgs: out string[] errorMsgs,
isContainWildcard: out bool _);
- foreach (string error in moduleErrorMsgs)
+ foreach (string error in errorMsgs)
{
WriteError(new ErrorRecord(
- new PSInvalidOperationException(error),
- "ErrorFilteringModuleNamesForUnsupportedWildcards",
+ new PSInvalidOperationException($"Wildcards are not supported for -Tag: {error}"),
+ "WildcardsUnsupportedForTag",
ErrorCategory.InvalidArgument,
this));
}
- if (moduleNamesToSearch.Length == 0)
+ // this catches the case where Name wasn't passed in as null or empty,
+ // but after filtering out unsupported wildcard names there are no elements left in tagsToSearch
+ if (tagsToSearch.Length == 0)
{
- moduleNamesToSearch = new string[] {"*"};
+ return;
}
-
- List foundPackages = _findHelper.FindByResourceName(
- name: moduleNamesToSearch,
- type: isSearchingForCommands? ResourceType.Command : ResourceType.DscResource,
- version: Version,
+
+ foreach (PSResourceInfo tagPkg in _findHelper.FindTag(
+ type: Type,
prerelease: Prerelease,
- tag: Tag,
- repository: Repository,
- credential: Credential,
- includeDependencies: IncludeDependencies);
-
- // if a single package contains multiple commands we are interested in, return a unique entry for each:
- // Command1 , PackageA
- // Command2 , PackageA
- foreach (string nameToSearch in commandOrDSCNamesToSearch)
+ tag: tagsToSearch,
+ repository: Repository))
{
- foreach (var package in foundPackages)
- {
- // this check ensures DSC names provided as a Command name won't get returned mistakenly
- // -CommandName "command1", "dsc1" <- (will not return or add DSC name)
- if ((isSearchingForCommands && package.Includes.Command.Contains(nameToSearch)) ||
- (!isSearchingForCommands && package.Includes.DscResource.Contains(nameToSearch)))
- {
- WriteObject(new PSCommandResourceInfo(nameToSearch, package));
- }
- }
+ WriteObject(tagPkg);
}
}
diff --git a/src/code/GetHelper.cs b/src/code/GetHelper.cs
index 49b24bc9b..e612a7e52 100644
--- a/src/code/GetHelper.cs
+++ b/src/code/GetHelper.cs
@@ -48,7 +48,16 @@ public IEnumerable GetInstalledPackages(
foreach (var pkg in pkgs)
{
// Filter on specific version.
- var nugetVersion = new NuGetVersion(pkg.Version);
+ pkg.AdditionalMetadata.TryGetValue("NormalizedVersion", out string normalizedVersion);
+
+ if (!NuGetVersion.TryParse(
+ value: normalizedVersion,
+ version: out NuGetVersion nugetVersion))
+ {
+ _cmdletPassedIn.WriteVerbose(String.Format("Package's normalized version '{0}' cannot be parsed into NuGetVersion.", normalizedVersion));
+ yield break;
+ }
+
var pkgVersionRange = new VersionRange(
minVersion: nugetVersion,
includeMinVersion: true,
diff --git a/src/code/GetPSResource.cs b/src/code/GetPSResource.cs
index f127cd672..543ec41c6 100644
--- a/src/code/GetPSResource.cs
+++ b/src/code/GetPSResource.cs
@@ -119,7 +119,7 @@ protected override void ProcessRecord()
{
WriteVerbose("Entering GetPSResource");
- var namesToSearch = Utils.ProcessNameWildcards(Name, out string[] errorMsgs, out bool _);
+ var namesToSearch = Utils.ProcessNameWildcards(Name, removeWildcardEntries:false, out string[] errorMsgs, out bool _);
foreach (string error in errorMsgs)
{
WriteError(new ErrorRecord(
diff --git a/src/code/IServerAPICalls.cs b/src/code/IServerAPICalls.cs
new file mode 100644
index 000000000..5c7b6f9a2
--- /dev/null
+++ b/src/code/IServerAPICalls.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using NuGet.Versioning;
+using System.Net.Http;
+using System.Runtime.ExceptionServices;
+
+public interface IServerAPICalls
+{
+ #region Methods
+ ///
+ /// Find method which allows for searching for all packages from a repository and returns latest version for each.
+ /// Examples: Search -Repository PSGallery
+ /// API call:
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsAbsoluteLatestVersion&includePrerelease=true
+ ///
+ string[] FindAll(bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for packages with tag from a repository and returns latest version for each.
+ /// Examples: Search -Tag "JSON" -Repository PSGallery
+ /// API call:
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsAbsoluteLatestVersion&searchTerm='tag:JSON'&includePrerelease=true
+ ///
+ string[] FindTag(string tag, bool includePrerelease, ResourceType _type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name and returns latest version.
+ /// Name: no wildcard support
+ /// Examples: Search "PowerShellGet"
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference)
+ ///
+ string FindName(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ string FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name with version range.
+ /// Name: no wildcard support
+ /// Version: supports wildcards
+ /// Examples: Search "PowerShellGet" "[3.0.0.0, 5.0.0.0]"
+ /// Search "PowerShellGet" "3.*"
+ /// API Call: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation note: Returns all versions, including prerelease ones. Later (in the API client side) we'll do filtering on the versions to satisfy what user provided.
+ ///
+ string[] FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ string[] FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name with specific version.
+ /// Name: no wildcard support
+ /// Version: no wildcard support
+ /// Examples: Search "PowerShellGet" "2.2.5"
+ /// API call: http://www.powershellgallery.com/api/v2/Packages(Id='PowerShellGet', Version='2.2.5')
+ ///
+ string[] FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, out ExceptionDispatchInfo edi);
+
+ //
+ /// Find method which allows for searching for single name with specific version.
+ /// Name: no wildcard support
+ /// Version: no wildcard support
+ /// Examples: Search "PowerShellGet" "2.2.5"
+ /// API call: http://www.powershellgallery.com/api/v2/Packages(Id='PowerShellGet', Version='2.2.5')
+ ///
+ string FindVersion(string packageName, string version, ResourceType type, out ExceptionDispatchInfo edi);
+
+ string FindVersionWithTag(string packageName, string version, string[] tags, ResourceType type, out ExceptionDispatchInfo edi);
+
+
+ ///
+ /// Installs specific package.
+ /// Name: no wildcard support.
+ /// Examples: Install "PowerShellGet"
+ /// Implementation Note: if prerelease: call IFindPSResource.FindName()
+ /// if not prerelease: https://www.powershellgallery.com/api/v2/package/Id (Returns latest stable)
+ ///
+ HttpContent InstallName(string packageName, bool includePrerelease, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Installs package with specific name and version.
+ /// Name: no wildcard support.
+ /// Version: no wildcard support.
+ /// Examples: Install "PowerShellGet" -Version "3.0.0.0"
+ /// Install "PowerShellGet" -Version "3.0.0-beta16"
+ /// API Call: https://www.powershellgallery.com/api/v2/package/Id/version (version can be prerelease)
+ ///
+ HttpContent InstallVersion(string packageName, string version, out ExceptionDispatchInfo edi);
+
+ #endregion
+}
diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs
index 7153ff86c..3ee86bd4e 100644
--- a/src/code/InstallHelper.cs
+++ b/src/code/InstallHelper.cs
@@ -4,7 +4,6 @@
using Microsoft.PowerShell.PowerShellGet.UtilClasses;
using MoreLinq.Extensions;
using NuGet.Common;
-using NuGet.Configuration;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Packaging.PackageExtraction;
@@ -19,6 +18,8 @@
using System.Linq;
using System.Management.Automation;
using System.Net;
+using System.Net.Http;
+using System.Runtime.ExceptionServices;
using System.Text.RegularExpressions;
using System.Threading;
@@ -40,36 +41,45 @@ internal class InstallHelper
private readonly PSCmdlet _cmdletPassedIn;
private List _pathsToInstallPkg;
private VersionRange _versionRange;
+ private NuGetVersion _nugetVersion;
+ private VersionType _versionType;
+ private string _versionString;
private bool _prerelease;
private bool _acceptLicense;
private bool _quiet;
private bool _reinstall;
private bool _force;
private bool _trustRepository;
- private PSCredential _credential;
private bool _asNupkg;
private bool _includeXml;
private bool _noClobber;
private bool _authenticodeCheck;
private bool _savePkg;
List _pathsToSearch;
- HashSet _pkgNamesToInstall;
+ List _pkgNamesToInstall;
private string _tmpPath;
+ private NetworkCredential _networkCredential;
+ private HashSet _packagesOnMachine;
#endregion
- #region Public methods
+ #region Public Methods
- public InstallHelper(PSCmdlet cmdletPassedIn)
+ public InstallHelper(PSCmdlet cmdletPassedIn, NetworkCredential networkCredential)
{
CancellationTokenSource source = new CancellationTokenSource();
_cancellationToken = source.Token;
_cmdletPassedIn = cmdletPassedIn;
+ _networkCredential = networkCredential;
}
- public List InstallPackages(
+ ///
+ /// This method calls is the starting point for install processes and is called by Install, Update and Save cmdlets.
+ ///
+ public IEnumerable InstallPackages(
string[] names,
VersionRange versionRange,
+ string versionString,
bool prerelease,
string[] repository,
bool acceptLicense,
@@ -78,7 +88,6 @@ public List InstallPackages(
bool force,
bool trustRepository,
bool noClobber,
- PSCredential credential,
bool asNupkg,
bool includeXml,
bool skipDependencyCheck,
@@ -86,12 +95,14 @@ public List InstallPackages(
bool savePkg,
List pathsToInstallPkg,
ScopeType? scope,
- string tmpPath)
+ string tmpPath,
+ HashSet pkgsInstalled)
{
_cmdletPassedIn.WriteVerbose(string.Format("Parameters passed in >>> Name: '{0}'; Version: '{1}'; Prerelease: '{2}'; Repository: '{3}'; " +
"AcceptLicense: '{4}'; Quiet: '{5}'; Reinstall: '{6}'; TrustRepository: '{7}'; NoClobber: '{8}'; AsNupkg: '{9}'; IncludeXml '{10}'; SavePackage '{11}'; TemporaryPath '{12}'",
string.Join(",", names),
versionRange != null ? (versionRange.OriginalString != null ? versionRange.OriginalString : string.Empty) : string.Empty,
+ versionString != null ? versionString : String.Empty,
prerelease.ToString(),
repository != null ? string.Join(",", repository) : string.Empty,
acceptLicense.ToString(),
@@ -104,8 +115,8 @@ public List InstallPackages(
savePkg.ToString(),
tmpPath ?? string.Empty));
-
_versionRange = versionRange;
+ _versionString = versionString ?? String.Empty;
_prerelease = prerelease;
_acceptLicense = acceptLicense || force;
_authenticodeCheck = authenticodeCheck;
@@ -114,16 +125,32 @@ public List InstallPackages(
_force = force;
_trustRepository = trustRepository || force;
_noClobber = noClobber;
- _credential = credential;
_asNupkg = asNupkg;
_includeXml = includeXml;
_savePkg = savePkg;
_pathsToInstallPkg = pathsToInstallPkg;
_tmpPath = tmpPath ?? Path.GetTempPath();
+ bool parsedAsNuGetVersion = NuGetVersion.TryParse(_versionString, out _nugetVersion);
+ if (parsedAsNuGetVersion)
+ {
+ _versionType = VersionType.SpecificVersion;
+ }
+ else if (!parsedAsNuGetVersion && _versionRange == null || _versionRange == VersionRange.All)
+ {
+ // if VersionRange == VersionRange.All then we wish to get latest package only (maps to VersionType.NoVersion)
+ _versionType = VersionType.NoVersion;
+ }
+ else
+ {
+ // versionRange is specified
+ _versionType = VersionType.VersionRange;
+ }
+
// Create list of installation paths to search.
_pathsToSearch = new List();
- _pkgNamesToInstall = new HashSet(names);
+ _pkgNamesToInstall = names.ToList();
+ _packagesOnMachine = pkgsInstalled;
// _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable)
// _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations
@@ -138,23 +165,26 @@ public List InstallPackages(
}
// Go through the repositories and see which is the first repository to have the pkg version available
- return ProcessRepositories(
+ List installedPkgs = ProcessRepositories(
repository: repository,
trustRepository: _trustRepository,
- credential: _credential,
skipDependencyCheck: skipDependencyCheck,
- scope: scope?? ScopeType.CurrentUser);
+ scope: scope ?? ScopeType.CurrentUser);
+
+ return installedPkgs;
}
#endregion
#region Private methods
- // This method calls iterates through repositories (by priority order) to search for the pkgs to install
+ ///
+ /// This method calls iterates through repositories (by priority order) to search for the packages to install.
+ /// It calls HTTP or NuGet API based install helper methods, according to repository type.
+ ///
private List ProcessRepositories(
string[] repository,
bool trustRepository,
- PSCredential credential,
bool skipDependencyCheck,
ScopeType scope)
{
@@ -162,45 +192,75 @@ private List ProcessRepositories(
var yesToAll = false;
var noToAll = false;
- var findHelper = new FindHelper(_cancellationToken, _cmdletPassedIn);
+ var findHelper = new FindHelper(_cancellationToken, _cmdletPassedIn, _networkCredential);
List allPkgsInstalled = new List();
bool sourceTrusted = false;
foreach (var repo in listOfRepositories)
{
- // If no more packages to install, then return
- if (_pkgNamesToInstall.Count == 0) return allPkgsInstalled;
+ // Explicitly passed in Credential takes precedence over repository CredentialInfo.
+ if (_networkCredential == null && repo.CredentialInfo != null)
+ {
+ PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
+ repo.Name,
+ repo.CredentialInfo,
+ _cmdletPassedIn);
+ var username = repoCredential.UserName;
+ var password = repoCredential.Password;
+
+ _networkCredential = new NetworkCredential(username, password);
+
+ _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repo.Name);
+ }
+
+ ServerApiCall currentServer = ServerFactory.GetServer(repo, _networkCredential);
+ ResponseUtil currentResponseUtil = ResponseUtilFactory.GetResponseUtil(repo);
+ bool installDepsForRepo = skipDependencyCheck;
+
+ // If no more packages to install, then return
+ if (_pkgNamesToInstall.Count == 0) {
+ return allPkgsInstalled;
+ }
+
string repoName = repo.Name;
_cmdletPassedIn.WriteVerbose(string.Format("Attempting to search for packages in '{0}'", repoName));
- // If it can't find the pkg in one repository, it'll look for it in the next repo in the list
- var isLocalRepo = repo.Uri.AbsoluteUri.StartsWith(Uri.UriSchemeFile + Uri.SchemeDelimiter, StringComparison.OrdinalIgnoreCase);
-
- // Finds parent packages and dependencies
- List pkgsFromRepoToInstall = findHelper.FindByResourceName(
- name: _pkgNamesToInstall.ToArray(),
- type: ResourceType.None,
- version: _versionRange?.OriginalString,
- prerelease: _prerelease,
- tag: null,
- repository: new string[] { repoName },
- credential: credential,
- includeDependencies: !skipDependencyCheck);
- if (pkgsFromRepoToInstall.Count == 0)
+ if (repo.ApiVersion == PSRepositoryInfo.APIVersion.v2 || repo.ApiVersion == PSRepositoryInfo.APIVersion.v3)
{
- _cmdletPassedIn.WriteVerbose(string.Format("None of the specified resources were found in the '{0}' repository.", repoName));
- continue;
+ if (repo.Trusted == false && !trustRepository && !_force)
+ {
+ _cmdletPassedIn.WriteVerbose("Checking if untrusted repository should be used");
+
+ if (!(yesToAll || noToAll))
+ {
+ // Prompt for installation of package from untrusted repository
+ var message = string.Format(CultureInfo.InvariantCulture, MsgInstallUntrustedPackage, repoName);
+ sourceTrusted = _cmdletPassedIn.ShouldContinue(message, MsgRepositoryNotTrusted, true, ref yesToAll, ref noToAll);
+ }
+ }
+
+ if (!sourceTrusted && !yesToAll)
+ {
+ continue;
+ }
+
+ if ((repo.ApiVersion == PSRepositoryInfo.APIVersion.v3) && (!installDepsForRepo))
+ {
+ _cmdletPassedIn.WriteWarning("Installing dependencies is not currently supported for V3 server protocol repositories. The package will be installed without installing dependencies.");
+ installDepsForRepo = true;
+ }
+
+ return HttpInstall(_pkgNamesToInstall.ToArray(), repo, currentServer, currentResponseUtil, scope, skipDependencyCheck, findHelper);
}
else
{
- // Check trust for repository where package was found.
// Source is only trusted if it's set at the repository level to be trusted, -TrustRepository flag is true, -Force flag is true
// OR the user issues trust interactively via console.
if (repo.Trusted == false && !trustRepository && !_force)
{
- _cmdletPassedIn.WriteVerbose(string.Format("Checking if untrusted repository '{0}' should be used", repoName));
+ _cmdletPassedIn.WriteVerbose("Checking if untrusted repository should be used");
if (!(yesToAll || noToAll))
{
@@ -208,71 +268,92 @@ private List ProcessRepositories(
var message = string.Format(CultureInfo.InvariantCulture, MsgInstallUntrustedPackage, repoName);
sourceTrusted = _cmdletPassedIn.ShouldContinue(message, MsgRepositoryNotTrusted, true, ref yesToAll, ref noToAll);
}
+ }
- if (sourceTrusted || yesToAll)
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Untrusted repository '{0}' accepted as trusted source.", repoName));
- }
- else
- {
- continue;
- }
+ if (!sourceTrusted && !yesToAll)
+ {
+ continue;
}
- }
- // Select the first package from each name group, which is guaranteed to be the latest version.
- // We should only have one version returned for each package name
- // e.g.:
- // PackageA (version 1.0)
- // PackageB (version 2.0)
- // PackageC (version 1.0)
- pkgsFromRepoToInstall = pkgsFromRepoToInstall.GroupBy(
- m => new { m.Name }).Select(
- group => group.First()).ToList();
+ _cmdletPassedIn.WriteVerbose("Untrusted repository accepted as trusted source.");
+
+ // If it can't find the pkg in one repository, it'll look for it in the next repo in the list
+ var isLocalRepo = repo.Uri.AbsoluteUri.StartsWith(Uri.UriSchemeFile + Uri.SchemeDelimiter, StringComparison.OrdinalIgnoreCase);
+
+ // Finds parent packages and dependencies
+ List pkgsFromRepoToInstall = findHelper.FindByResourceName(
+ name: _pkgNamesToInstall.ToArray(),
+ type: ResourceType.None,
+ versionRange: _versionRange,
+ nugetVersion: _nugetVersion,
+ versionType: _versionType,
+ version: _versionRange?.OriginalString,
+ prerelease: _prerelease,
+ tag: null,
+ repository: new string[] { repoName },
+ includeDependencies: !installDepsForRepo).ToList();
+
+ if (pkgsFromRepoToInstall.Count == 0)
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("None of the specified resources were found in the '{0}' repository.", repoName));
+ continue;
+ }
- // Check to see if the pkgs (including dependencies) are already installed (ie the pkg is installed and the version satisfies the version range provided via param)
- if (!_reinstall)
- {
- pkgsFromRepoToInstall = FilterByInstalledPkgs(pkgsFromRepoToInstall);
- }
+ // Select the first package from each name group, which is guaranteed to be the latest version.
+ // We should only have one version returned for each package name
+ // e.g.:
+ // PackageA (version 1.0)
+ // PackageB (version 2.0)
+ // PackageC (version 1.0)
+ // pkgsFromRepoToInstall = pkgsFromRepoToInstall.GroupBy(
+ // m => new { m.Name }).Select(
+ // group => group.First()).ToList();
+
+ // Check to see if the pkgs (including dependencies) are already installed (ie the pkg is installed and the version satisfies the version range provided via param)
+ if (!_reinstall)
+ {
+ pkgsFromRepoToInstall = FilterByInstalledPkgs(pkgsFromRepoToInstall);
+ }
- if (pkgsFromRepoToInstall.Count is 0)
- {
- continue;
- }
+ if (pkgsFromRepoToInstall.Count is 0)
+ {
+ continue;
+ }
- List pkgsInstalled = InstallPackage(
- pkgsFromRepoToInstall,
- repoName,
- repo.Uri.AbsoluteUri,
- repo.CredentialInfo,
- credential,
- isLocalRepo,
- scope: scope);
+ List pkgsInstalled = InstallPackage(
+ pkgsFromRepoToInstall,
+ repoName,
+ repo.Uri.AbsoluteUri,
+ repo.CredentialInfo,
+ isLocalRepo,
+ scope: scope);
- foreach (PSResourceInfo pkg in pkgsInstalled)
- {
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
- }
+ foreach (PSResourceInfo pkg in pkgsInstalled)
+ {
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ }
- allPkgsInstalled.AddRange(pkgsInstalled);
- }
+ allPkgsInstalled.AddRange(pkgsInstalled);
+ }
- // At this only package names left were those which could not be found in registered repositories
- foreach (string pkgName in _pkgNamesToInstall)
- {
- string message = !sourceTrusted ? $"Package '{pkgName}' with requested version range '{_versionRange.ToString()}' could not be found in any trusted repositories" :
- $"Package '{pkgName}' with requested version range '{_versionRange.ToString()}' could not be installed as it was not found in any registered repositories";
+ // At this only package names left were those which could not be found in registered repositories
+ foreach (string pkgName in _pkgNamesToInstall)
+ {
+ string message = !sourceTrusted ? $"Package '{pkgName}' with requested version range '{_versionRange.ToString()}' could not be found in any trusted repositories" :
+ $"Package '{pkgName}' with requested version range '{_versionRange.ToString()}' could not be installed as it was not found in any registered repositories";
- var ex = new ArgumentException(message);
- var ResourceNotFoundError = new ErrorRecord(ex, "ResourceNotFoundError", ErrorCategory.ObjectNotFound, null);
- _cmdletPassedIn.WriteError(ResourceNotFoundError);
+ var ex = new ArgumentException(message);
+ var ResourceNotFoundError = new ErrorRecord(ex, "ResourceNotFoundError", ErrorCategory.ObjectNotFound, null);
+ _cmdletPassedIn.WriteError(ResourceNotFoundError);
+ }
}
return allPkgsInstalled;
}
- // Check if any of the pkg versions are already installed, if they are we'll remove them from the list of packages to install
+ ///
+ /// Checks if any of the package versions are already installed and if they are removes them from the list of packages to install.
+ ///
private List FilterByInstalledPkgs(List packages)
{
// Package install paths.
@@ -310,22 +391,15 @@ private List FilterByInstalledPkgs(List packages
}
else
{
- // Only write warning if the package is not a dependency package being installed.
- if (_pkgNamesToInstall.Contains(pkg.Name)) {
- _cmdletPassedIn.WriteWarning(
- string.Format("Resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
- pkg.Name,
- pkg.Version));
- }
- else {
- _cmdletPassedIn.WriteVerbose(
- string.Format("Dependency resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
- pkg.Name,
- pkg.Version));
- }
+ // Remove from tracking list of packages to install.
+ pkg.AdditionalMetadata.TryGetValue("NormalizedVersion", out string normalizedVersion);
+ _cmdletPassedIn.WriteWarning(
+ string.Format("Resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
+ pkg.Name,
+ normalizedVersion));
// Remove from tracking list of packages to install.
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
}
}
@@ -333,14 +407,938 @@ private List FilterByInstalledPkgs(List packages
}
///
- /// Install provided list of packages, which include Dependent packages if requested.
+ /// Deletes temp directory and is called at end of install process.
+ ///
+ private bool TryDeleteDirectory(
+ string tempInstallPath,
+ out ErrorRecord errorMsg)
+ {
+ errorMsg = null;
+
+ try
+ {
+ Utils.DeleteDirectory(tempInstallPath);
+ }
+ catch (Exception e)
+ {
+ var TempDirCouldNotBeDeletedError = new ErrorRecord(e, "errorDeletingTempInstallPath", ErrorCategory.InvalidResult, null);
+ errorMsg = TempDirCouldNotBeDeletedError;
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Moves file from the temp install path to desination path for install.
+ ///
+ private void MoveFilesIntoInstallPath(
+ PSResourceInfo pkgInfo,
+ bool isModule,
+ bool isLocalRepo,
+ string dirNameVersion,
+ string tempInstallPath,
+ string installPath,
+ string newVersion,
+ string moduleManifestVersion,
+ string scriptPath)
+ {
+ // Creating the proper installation path depending on whether pkg is a module or script
+ var newPathParent = isModule ? Path.Combine(installPath, pkgInfo.Name) : installPath;
+ var finalModuleVersionDir = isModule ? Path.Combine(installPath, pkgInfo.Name, moduleManifestVersion) : installPath;
+
+ // If script, just move the files over, if module, move the version directory over
+ var tempModuleVersionDir = (!isModule || isLocalRepo) ? dirNameVersion
+ : Path.Combine(tempInstallPath, pkgInfo.Name.ToLower(), newVersion);
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Installation source path is: '{0}'", tempModuleVersionDir));
+ _cmdletPassedIn.WriteVerbose(string.Format("Installation destination path is: '{0}'", finalModuleVersionDir));
+
+ if (isModule)
+ {
+ // If new path does not exist
+ if (!Directory.Exists(newPathParent))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Attempting to move '{0}' to '{1}'", tempModuleVersionDir, finalModuleVersionDir));
+ Directory.CreateDirectory(newPathParent);
+ Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir);
+ }
+ else
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Temporary module version directory is: '{0}'", tempModuleVersionDir));
+
+ if (Directory.Exists(finalModuleVersionDir))
+ {
+ // Delete the directory path before replacing it with the new module.
+ // If deletion fails (usually due to binary file in use), then attempt restore so that the currently
+ // installed module is not corrupted.
+ _cmdletPassedIn.WriteVerbose(string.Format("Attempting to delete with restore on failure.'{0}'", finalModuleVersionDir));
+ Utils.DeleteDirectoryWithRestore(finalModuleVersionDir);
+ }
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Attempting to move '{0}' to '{1}'", tempModuleVersionDir, finalModuleVersionDir));
+ Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir);
+ }
+ }
+ else if (_asNupkg)
+ {
+ foreach (string file in Directory.GetFiles(tempInstallPath))
+ {
+ string fileName = Path.GetFileName(file);
+ string newFileName = string.Equals(Path.GetExtension(file), ".zip", StringComparison.OrdinalIgnoreCase) ?
+ $"{Path.GetFileNameWithoutExtension(file)}.nupkg" : fileName;
+
+ Utils.MoveFiles(Path.Combine(tempInstallPath, fileName), Path.Combine(installPath, newFileName));
+ }
+ }
+ else
+ {
+ if (!_savePkg)
+ {
+ // Need to delete old xml files because there can only be 1 per script
+ var scriptXML = pkgInfo.Name + "_InstalledScriptInfo.xml";
+ _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(installPath, "InstalledScriptInfos", scriptXML))));
+ if (File.Exists(Path.Combine(installPath, "InstalledScriptInfos", scriptXML)))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting script metadata XML"));
+ File.Delete(Path.Combine(installPath, "InstalledScriptInfos", scriptXML));
+ }
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML)));
+ Utils.MoveFiles(Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML));
+
+ // Need to delete old script file, if that exists
+ _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt))));
+ if (File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting script file"));
+ File.Delete(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
+ }
+ }
+
+ _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)));
+ Utils.MoveFiles(scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
+ }
+ }
+
+ #endregion
+
+ #region Private HTTP Methods
+
+ ///
+ /// Iterates through package names passed in and calls method to install each package and their dependencies.
+ ///
+ private List HttpInstall(
+ string[] pkgNamesToInstall,
+ PSRepositoryInfo repository,
+ ServerApiCall currentServer,
+ ResponseUtil currentResponseUtil,
+ ScopeType scope,
+ bool skipDependencyCheck,
+ FindHelper findHelper)
+ {
+ List pkgsSuccessfullyInstalled = new List();
+
+ // Install parent package to the temp directory,
+ // Get the dependencies from the installed package,
+ // Install all dependencies to temp directory.
+ // If a single dependency fails to install, roll back by deleting the temp directory.
+ foreach (var parentPackage in pkgNamesToInstall)
+ {
+ string tempInstallPath = CreateInstallationTempPath();
+
+ try
+ {
+ // Hashtable has the key as the package name
+ // and value as a Hashtable of specific package info:
+ // packageName, { version = "", isScript = "", isModule = "", pkg = "", etc. }
+ // Install parent package to the temp directory.
+ Hashtable packagesHash = HttpInstallPackage(
+ searchVersionType: _versionType,
+ specificVersion: _nugetVersion,
+ versionRange: _versionRange,
+ pkgNameToInstall: parentPackage,
+ repository: repository,
+ currentServer: currentServer,
+ currentResponseUtil: currentResponseUtil,
+ tempInstallPath: tempInstallPath,
+ packagesHash: new Hashtable(StringComparer.InvariantCultureIgnoreCase),
+ edi: out ExceptionDispatchInfo edi);
+
+ // At this point parent package is installed to temp path.
+ if (edi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(edi.SourceException, "InstallPackageFailure", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+
+ if (packagesHash.Count == 0) {
+ continue;
+ }
+
+ Hashtable parentPkgInfo = packagesHash[parentPackage] as Hashtable;
+ PSResourceInfo parentPkgObj = parentPkgInfo["psResourceInfoPkg"] as PSResourceInfo;
+
+ if (!skipDependencyCheck)
+ {
+ if (currentServer.repository.ApiVersion == PSRepositoryInfo.APIVersion.v3)
+ {
+ _cmdletPassedIn.WriteWarning("Installing dependencies is not currently supported for V3 server protocol repositories. The package will be installed without installing dependencies.");
+ }
+
+ HashSet myHash = new HashSet(StringComparer.OrdinalIgnoreCase);
+ // Get the dependencies from the installed package.
+ if (parentPkgObj.Dependencies.Length > 0)
+ {
+ foreach (PSResourceInfo depPkg in findHelper.HttpFindDependencyPackages(currentServer, currentResponseUtil, parentPkgObj, repository, myHash))
+ {
+ if (String.Equals(depPkg.Name, parentPkgObj.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ NuGetVersion depVersion = null;
+ if (depPkg.AdditionalMetadata.ContainsKey("NormalizedVersion"))
+ {
+ if (!NuGetVersion.TryParse(depPkg.AdditionalMetadata["NormalizedVersion"] as string, out depVersion))
+ {
+ NuGetVersion.TryParse(depPkg.Version.ToString(), out depVersion);
+ }
+ }
+
+ packagesHash = HttpInstallPackage(
+ searchVersionType: VersionType.SpecificVersion,
+ specificVersion: depVersion,
+ versionRange: null,
+ pkgNameToInstall: depPkg.Name,
+ repository: repository,
+ currentServer: currentServer,
+ currentResponseUtil: currentResponseUtil,
+ tempInstallPath: tempInstallPath,
+ packagesHash: packagesHash,
+ edi: out ExceptionDispatchInfo installPackageEdi);
+
+ if (installPackageEdi != null)
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(installPackageEdi.SourceException, "InstallDependencyPackageFailure", ErrorCategory.InvalidOperation, this));
+ continue;
+ }
+ }
+ }
+ }
+
+ // Parent package and dependencies are now installed to temp directory.
+ // Try to move all package directories from temp directory to final destination.
+ if (!TryMoveInstallContent(tempInstallPath, scope, packagesHash))
+ {
+ _cmdletPassedIn.WriteError(new ErrorRecord(new InvalidOperationException(), "InstallPackageTryMoveContentFailure", ErrorCategory.InvalidOperation, this));
+ }
+ else
+ {
+ foreach (string pkgName in packagesHash.Keys)
+ {
+ Hashtable pkgInfo = packagesHash[pkgName] as Hashtable;
+ pkgsSuccessfullyInstalled.Add(pkgInfo["psResourceInfoPkg"] as PSResourceInfo);
+
+ // Add each pkg to _packagesOnMachine (ie pkgs fully installed on the machine).
+ _packagesOnMachine.Add(Utils.CreateHashSetKey(pkgName, pkgInfo["pkgVersion"].ToString()));
+ }
+ }
+ }
+ finally
+ {
+ DeleteInstallationTempPath(tempInstallPath);
+ }
+ }
+
+ return pkgsSuccessfullyInstalled;
+ }
+
+ ///
+ /// Installs a single package to the temporary path.
+ ///
+ private Hashtable HttpInstallPackage(
+ VersionType searchVersionType,
+ NuGetVersion specificVersion,
+ VersionRange versionRange,
+ string pkgNameToInstall,
+ PSRepositoryInfo repository,
+ ServerApiCall currentServer,
+ ResponseUtil currentResponseUtil,
+ string tempInstallPath,
+ Hashtable packagesHash,
+ out ExceptionDispatchInfo edi)
+ {
+ //List packagesToInstall = new List();
+ string[] responses = Utils.EmptyStrArray;
+ edi = null;
+
+ switch (searchVersionType)
+ {
+ case VersionType.VersionRange:
+ responses = currentServer.FindVersionGlobbing(pkgNameToInstall, versionRange, _prerelease, ResourceType.None, getOnlyLatest: true, out ExceptionDispatchInfo findVersionGlobbingEdi);
+ // Server level globbing API will not populate edi for empty response, so must check for empty response and early out
+ if (findVersionGlobbingEdi != null || responses.Length == 0)
+ {
+ edi = findVersionGlobbingEdi;
+ return packagesHash;
+ }
+
+ break;
+
+ case VersionType.SpecificVersion:
+ string nugetVersionString = specificVersion.ToNormalizedString(); // 3.0.17-beta
+
+ string findVersionResponse = currentServer.FindVersion(pkgNameToInstall, nugetVersionString, ResourceType.None, out ExceptionDispatchInfo findVersionEdi);
+ responses = new string[] { findVersionResponse };
+ if (findVersionEdi != null)
+ {
+ edi = findVersionEdi;
+ return packagesHash;
+ }
+
+ break;
+
+ default:
+ // VersionType.NoVersion
+ string findNameResponse = currentServer.FindName(pkgNameToInstall, _prerelease, ResourceType.None, out ExceptionDispatchInfo findNameEdi);
+ responses = new string[] { findNameResponse };
+ if (findNameEdi != null)
+ {
+ edi = findNameEdi;
+ return packagesHash;
+ }
+
+ break;
+ }
+
+ PSResourceResult currentResult = currentResponseUtil.ConvertToPSResourceResult(responses: responses).First();
+
+ if (!String.IsNullOrEmpty(currentResult.errorMsg))
+ {
+ // V2Server API calls will return non-empty response when package is not found but fail at conversion time
+ edi = ExceptionDispatchInfo.Capture(new InvalidOrEmptyResponse($"Package for installation could not be found due to: {currentResult.errorMsg}"));
+ return packagesHash;
+ }
+
+ PSResourceInfo pkgToInstall = currentResult.returnedObject;
+ pkgToInstall.RepositorySourceLocation = repository.Uri.ToString();
+ pkgToInstall.AdditionalMetadata.TryGetValue("NormalizedVersion", out string pkgVersion);
+
+ // Check to see if the pkg is already installed (ie the pkg is installed and the version satisfies the version range provided via param)
+ if (!_reinstall)
+ {
+ string currPkgNameVersion = Utils.CreateHashSetKey(pkgToInstall.Name, pkgToInstall.Version.ToString());
+ if (_packagesOnMachine.Contains(currPkgNameVersion))
+ {
+ _cmdletPassedIn.WriteWarning(
+ string.Format("Resource '{0}' with version '{1}' is already installed. If you would like to reinstall, please run the cmdlet again with the -Reinstall parameter",
+ pkgToInstall.Name,
+ pkgVersion));
+ return packagesHash;
+ }
+ }
+
+ // Download the package.
+ string pkgName = pkgToInstall.Name;
+ HttpContent responseContent;
+
+ if (searchVersionType == VersionType.NoVersion && !_prerelease)
+ {
+ responseContent = currentServer.InstallName(pkgName, _prerelease, out ExceptionDispatchInfo installNameEdi);
+ if (installNameEdi != null)
+ {
+ edi = installNameEdi;
+ return packagesHash;
+ }
+ }
+ else
+ {
+ responseContent = currentServer.InstallVersion(pkgName, pkgVersion, out ExceptionDispatchInfo installVersionEdi);
+ if (installVersionEdi != null)
+ {
+ edi = installVersionEdi;
+ return packagesHash;
+ }
+ }
+
+ Hashtable updatedPackagesHash;
+ ErrorRecord error;
+ bool installedToTempPathSuccessfully = _asNupkg ? TrySaveNupkgToTempPath(responseContent, tempInstallPath, pkgName, pkgVersion, pkgToInstall, packagesHash, out updatedPackagesHash, out error) :
+ TryInstallToTempPath(responseContent, tempInstallPath, pkgName, pkgVersion, pkgToInstall, packagesHash, out updatedPackagesHash, out error);
+
+ if (!installedToTempPathSuccessfully)
+ {
+ edi = ExceptionDispatchInfo.Capture(error.Exception);
+ return packagesHash;
+ }
+
+ return updatedPackagesHash;
+ }
+
+ ///
+ /// Creates a temporary path used for installation before moving package to its final location.
+ ///
+ private string CreateInstallationTempPath()
+ {
+ var tempInstallPath = Path.Combine(_tmpPath, Guid.NewGuid().ToString());
+
+ try
+ {
+ var dir = Directory.CreateDirectory(tempInstallPath); // should check it gets created properly
+ // To delete file attributes from the existing ones get the current file attributes first and use AND (&) operator
+ // with a mask (bitwise complement of desired attributes combination).
+ // TODO: check the attributes and if it's read only then set it
+ // attribute may be inherited from the parent
+ // TODO: are there Linux accommodations we need to consider here?
+ dir.Attributes &= ~FileAttributes.ReadOnly;
+ }
+ catch (Exception e)
+ {
+ // catch more specific exception first
+ _cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
+ new ArgumentException($"Temporary folder for installation could not be created or set due to: {e.Message}"),
+ "TempFolderCreationError",
+ ErrorCategory.InvalidOperation,
+ this));
+ }
+
+ return tempInstallPath;
+ }
+
+ ///
+ /// Deletes the temporary path used for intermediary installation.
+ ///
+ private void DeleteInstallationTempPath(string tempInstallPath)
+ {
+ if (Directory.Exists(tempInstallPath))
+ {
+ // Delete the temp directory and all its contents
+ _cmdletPassedIn.WriteVerbose(string.Format("Attempting to delete '{0}'", tempInstallPath));
+ if (!TryDeleteDirectory(tempInstallPath, out ErrorRecord errorMsg))
+ {
+ _cmdletPassedIn.WriteError(errorMsg);
+ }
+ else
+ {
+ _cmdletPassedIn.WriteVerbose(String.Format("Successfully deleted '{0}'", tempInstallPath));
+ }
+ }
+ }
+
+ ///
+ /// Attempts to take installed HTTP response content and move it into a temporary install path on the machine.
+ ///
+ private bool TryInstallToTempPath(
+ HttpContent responseContent,
+ string tempInstallPath,
+ string pkgName,
+ string normalizedPkgVersion,
+ PSResourceInfo pkgToInstall,
+ Hashtable packagesHash,
+ out Hashtable updatedPackagesHash,
+ out ErrorRecord error)
+ {
+ error = null;
+ updatedPackagesHash = packagesHash;
+ try
+ {
+ var pathToFile = Path.Combine(tempInstallPath, $"{pkgName}.{normalizedPkgVersion}.zip");
+ using var content = responseContent.ReadAsStreamAsync().Result;
+ using var fs = File.Create(pathToFile);
+ content.Seek(0, System.IO.SeekOrigin.Begin);
+ content.CopyTo(fs);
+ fs.Close();
+
+ // Expand the zip file
+ var pkgVersion = pkgToInstall.Version.ToString();
+ var tempDirNameVersion = Path.Combine(tempInstallPath, pkgName.ToLower(), pkgVersion);
+ Directory.CreateDirectory(tempDirNameVersion);
+ System.IO.Compression.ZipFile.ExtractToDirectory(pathToFile, tempDirNameVersion);
+
+ File.Delete(pathToFile);
+
+ var moduleManifest = Path.Combine(tempDirNameVersion, pkgName + PSDataFileExt);
+ var scriptPath = Path.Combine(tempDirNameVersion, pkgName + PSScriptFileExt);
+
+ bool isModule = File.Exists(moduleManifest);
+ bool isScript = File.Exists(scriptPath);
+
+ if (!isModule && !isScript) {
+ scriptPath = "";
+ }
+
+ // TODO: add pkg validation when we figure out consistent/defined way to do so
+ if (_authenticodeCheck && !AuthenticodeSignature.CheckAuthenticodeSignature(
+ pkgName,
+ tempDirNameVersion,
+ _cmdletPassedIn,
+ out error))
+ {
+ return false;
+ }
+
+ string installPath = string.Empty;
+ if (isModule)
+ {
+ installPath = _pathsToInstallPkg.Find(path => path.EndsWith("Modules", StringComparison.InvariantCultureIgnoreCase));
+
+ if (!File.Exists(moduleManifest))
+ {
+ var message = String.Format("{0} package could not be installed with error: Module manifest file: {1} does not exist. This is not a valid PowerShell module.", pkgName, moduleManifest);
+ var ex = new ArgumentException(message);
+ error = new ErrorRecord(ex, "psdataFileNotExistError", ErrorCategory.ReadError, null);
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+
+ return false;
+ }
+
+ if (!Utils.TryReadManifestFile(
+ manifestFilePath: moduleManifest,
+ manifestInfo: out Hashtable parsedMetadataHashtable,
+ error: out Exception manifestReadError))
+ {
+ error = new ErrorRecord(
+ exception: manifestReadError,
+ errorId: "ManifestFileReadParseError",
+ errorCategory: ErrorCategory.ReadError,
+ this);
+
+ return false;
+ }
+
+ // Accept License verification
+ if (!_savePkg && !CallAcceptLicense(pkgToInstall, moduleManifest, tempInstallPath, pkgVersion, out error))
+ {
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgToInstall.Name, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+
+ // If NoClobber is specified, ensure command clobbering does not happen
+ if (_noClobber && DetectClobber(pkgName, parsedMetadataHashtable, out error))
+ {
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+ else if (isScript)
+ {
+ installPath = _pathsToInstallPkg.Find(path => path.EndsWith("Scripts", StringComparison.InvariantCultureIgnoreCase));
+
+ // is script
+ if (!PSScriptFileInfo.TryTestPSScriptFile(
+ scriptFileInfoPath: scriptPath,
+ parsedScript: out PSScriptFileInfo scriptToInstall,
+ out ErrorRecord[] parseScriptFileErrors,
+ out string[] _))
+ {
+ foreach (ErrorRecord parseError in parseScriptFileErrors)
+ {
+ _cmdletPassedIn.WriteError(parseError);
+ }
+
+ var ex = new InvalidOperationException($"PSScriptFile could not be parsed");
+ error = new ErrorRecord(ex, "psScriptParseError", ErrorCategory.ReadError, null);
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+
+ return false;
+ }
+ }
+ else
+ {
+ // This package is not a PowerShell package (eg a resource from the NuGet Gallery).
+ installPath = _pathsToInstallPkg.Find(path => path.EndsWith("Modules", StringComparison.InvariantCultureIgnoreCase));
+
+ _cmdletPassedIn.WriteVerbose($"This resource is not a PowerShell package and will be installed to the modules path: {installPath}.");
+ isModule = true;
+ }
+
+ installPath = _savePkg ? _pathsToInstallPkg.First() : installPath;
+
+ DeleteExtraneousFiles(pkgName, tempDirNameVersion);
+
+ if (_includeXml)
+ {
+ if (!CreateMetadataXMLFile(tempDirNameVersion, installPath, pkgToInstall, isModule, out error))
+ {
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgToInstall.Name, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+
+ if (!updatedPackagesHash.ContainsKey(pkgName))
+ {
+ // Add pkg info to hashtable.
+ updatedPackagesHash.Add(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase)
+ {
+ { "isModule", isModule },
+ { "isScript", isScript },
+ { "psResourceInfoPkg", pkgToInstall },
+ { "tempDirNameVersionPath", tempDirNameVersion },
+ { "pkgVersion", pkgVersion },
+ { "scriptPath", scriptPath },
+ { "installPath", installPath }
+ });
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ error = new ErrorRecord(
+ new PSInvalidOperationException(
+ message: $"Unable to successfully install package '{pkgName}': '{e.Message}' to temporary installation path.",
+ innerException: e),
+ "InstallPackageFailed",
+ ErrorCategory.InvalidOperation,
+ _cmdletPassedIn);
+
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+
+ ///
+ /// Attempts to take Http response content and move the .nupkg into a temporary install path on the machine.
+ ///
+ private bool TrySaveNupkgToTempPath(
+ HttpContent responseContent,
+ string tempInstallPath,
+ string pkgName,
+ string normalizedPkgVersion,
+ PSResourceInfo pkgToInstall,
+ Hashtable packagesHash,
+ out Hashtable updatedPackagesHash,
+ out ErrorRecord error)
+ {
+ error = null;
+ updatedPackagesHash = packagesHash;
+
+ try
+ {
+ var pathToFile = Path.Combine(tempInstallPath, $"{pkgName}.{normalizedPkgVersion}.zip");
+ using var content = responseContent.ReadAsStreamAsync().Result;
+ using var fs = File.Create(pathToFile);
+ content.Seek(0, System.IO.SeekOrigin.Begin);
+ content.CopyTo(fs);
+ fs.Close();
+
+ string installPath = _pathsToInstallPkg.First();
+ if (_includeXml)
+ {
+ if (!CreateMetadataXMLFile(tempInstallPath, installPath, pkgToInstall, isModule: true, out error))
+ {
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+
+ if (!updatedPackagesHash.ContainsKey(pkgName))
+ {
+ // Add pkg info to hashtable.
+ updatedPackagesHash.Add(pkgName, new Hashtable(StringComparer.InvariantCultureIgnoreCase)
+ {
+ { "isModule", "" },
+ { "isScript", "" },
+ { "psResourceInfoPkg", pkgToInstall },
+ { "tempDirNameVersionPath", tempInstallPath },
+ { "pkgVersion", "" },
+ { "scriptPath", "" },
+ { "installPath", installPath }
+ });
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ error = new ErrorRecord(
+ new PSInvalidOperationException(
+ message: $"Unable to successfully save .nupkg '{pkgName}': '{e.Message}' to temporary installation path.",
+ innerException: e),
+ "SaveNupkgFailed",
+ ErrorCategory.InvalidOperation,
+ _cmdletPassedIn);
+
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+
+ ///
+ /// Moves package files/directories from the temp install path into the final install path location.
+ ///
+ private bool TryMoveInstallContent(string tempInstallPath, ScopeType scope, Hashtable packagesHash)
+ {
+ foreach (string pkgName in packagesHash.Keys)
+ {
+ Hashtable pkgInfo = packagesHash[pkgName] as Hashtable;
+ bool isModule = (pkgInfo["isModule"] as bool?) ?? false;
+ bool isScript = (pkgInfo["isScript"] as bool?) ?? false;
+ PSResourceInfo pkgToInstall = pkgInfo["psResourceInfoPkg"] as PSResourceInfo;
+ string tempDirNameVersion = pkgInfo["tempDirNameVersionPath"] as string;
+ string pkgVersion = pkgInfo["pkgVersion"] as string;
+ string scriptPath = pkgInfo["scriptPath"] as string;
+ string installPath = pkgInfo["installPath"] as string;
+
+ try
+ {
+ MoveFilesIntoInstallPath(
+ pkgToInstall,
+ isModule,
+ isLocalRepo: false, // false for HTTP repo
+ tempDirNameVersion,
+ tempInstallPath,
+ installPath,
+ newVersion: pkgVersion, // would not have prerelease label in this string
+ moduleManifestVersion: pkgVersion,
+ scriptPath);
+
+ _cmdletPassedIn.WriteVerbose(String.Format("Successfully installed package '{0}' to location '{1}'", pkgName, installPath));
+
+ if (!_savePkg && isScript)
+ {
+ string installPathwithBackSlash = installPath + "\\";
+ string envPATHVarValue = Environment.GetEnvironmentVariable("PATH",
+ scope == ScopeType.CurrentUser ? EnvironmentVariableTarget.User : EnvironmentVariableTarget.Machine);
+
+ if (!envPATHVarValue.Contains(installPath) && !envPATHVarValue.Contains(installPathwithBackSlash))
+ {
+ _cmdletPassedIn.WriteWarning(String.Format(ScriptPATHWarning, scope, installPath));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _cmdletPassedIn.WriteError(
+ new ErrorRecord(
+ new PSInvalidOperationException(
+ message: $"Unable to successfully install package '{pkgName}': '{e.Message}'",
+ innerException: e),
+ "InstallPackageFailed",
+ ErrorCategory.InvalidOperation,
+ _cmdletPassedIn));
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// If the package requires license to be accepted, checks if the user has accepted it.
+ ///
+ private bool CallAcceptLicense(PSResourceInfo p, string moduleManifest, string tempInstallPath, string newVersion, out ErrorRecord error)
+ {
+ error = null;
+ var requireLicenseAcceptance = false;
+ var success = true;
+
+ if (File.Exists(moduleManifest))
+ {
+ using (StreamReader sr = new StreamReader(moduleManifest))
+ {
+ var text = sr.ReadToEnd();
+
+ var pattern = "RequireLicenseAcceptance\\s*=\\s*\\$true";
+ var patternToSkip1 = "#\\s*RequireLicenseAcceptance\\s*=\\s*\\$true";
+ var patternToSkip2 = "\\*\\s*RequireLicenseAcceptance\\s*=\\s*\\$true";
+
+ Regex rgx = new Regex(pattern);
+ Regex rgxComment1 = new Regex(patternToSkip1);
+ Regex rgxComment2 = new Regex(patternToSkip2);
+ if (rgx.IsMatch(text) && !rgxComment1.IsMatch(text) && !rgxComment2.IsMatch(text))
+ {
+ requireLicenseAcceptance = true;
+ }
+ }
+
+ // Licesnse agreement processing
+ if (requireLicenseAcceptance)
+ {
+ // If module requires license acceptance and -AcceptLicense is not passed in, display prompt
+ if (!_acceptLicense)
+ {
+ var PkgTempInstallPath = Path.Combine(tempInstallPath, p.Name, newVersion);
+ var LicenseFilePath = Path.Combine(PkgTempInstallPath, "License.txt");
+
+ if (!File.Exists(LicenseFilePath))
+ {
+ var exMessage = String.Format("{0} package could not be installed with error: License.txt not found. License.txt must be provided when user license acceptance is required.", p.Name);
+ var ex = new ArgumentException(exMessage);
+ var acceptLicenseError = new ErrorRecord(ex, "LicenseTxtNotFound", ErrorCategory.ObjectNotFound, null);
+ error = acceptLicenseError;
+ success = false;
+ return success;
+ }
+
+ // Otherwise read LicenseFile
+ string licenseText = System.IO.File.ReadAllText(LicenseFilePath);
+ var acceptanceLicenseQuery = $"Do you accept the license terms for module '{p.Name}'.";
+ var message = licenseText + "`r`n" + acceptanceLicenseQuery;
+
+ var title = "License Acceptance";
+ var yesToAll = false;
+ var noToAll = false;
+ var shouldContinueResult = _cmdletPassedIn.ShouldContinue(message, title, true, ref yesToAll, ref noToAll);
+
+ if (shouldContinueResult || yesToAll)
+ {
+ _acceptLicense = true;
+ }
+ }
+
+ // Check if user agreed to license terms, if they didn't then throw error, otherwise continue to install
+ if (!_acceptLicense)
+ {
+ var message = String.Format("{0} package could not be installed with error: License Acceptance is required for module '{0}'. Please specify '-AcceptLicense' to perform this operation.", p.Name);
+ var ex = new ArgumentException(message);
+ var acceptLicenseError = new ErrorRecord(ex, "ForceAcceptLicense", ErrorCategory.InvalidArgument, null);
+ error = acceptLicenseError;
+ success = false;
+ }
+ }
+ }
+
+ return success;
+ }
+
+ ///
+ /// If the option for no clobber is specified, ensures that commands or cmdlets are not being clobbered.
+ ///
+ private bool DetectClobber(string pkgName, Hashtable parsedMetadataHashtable, out ErrorRecord error)
+ {
+ error = null;
+ bool foundClobber = false;
+
+ // Get installed modules, then get all possible paths
+ // selectPrereleaseOnly is false because even if Prerelease is true we want to include both stable and prerelease, would never select prerelease only.
+ GetHelper getHelper = new GetHelper(_cmdletPassedIn);
+ IEnumerable pkgsAlreadyInstalled = getHelper.GetPackagesFromPath(
+ name: new string[] { "*" },
+ versionRange: VersionRange.All,
+ pathsToSearch: _pathsToSearch,
+ selectPrereleaseOnly: false);
+
+ List listOfCmdlets = new List();
+ foreach (var cmdletName in parsedMetadataHashtable["CmdletsToExport"] as object[])
+ {
+ listOfCmdlets.Add(cmdletName as string);
+ }
+
+ foreach (var pkg in pkgsAlreadyInstalled)
+ {
+ List duplicateCmdlets = new List();
+ List duplicateCmds = new List();
+ // See if any of the cmdlets or commands in the pkg we're trying to install exist within a package that's already installed
+ if (pkg.Includes.Cmdlet != null && pkg.Includes.Cmdlet.Any())
+ {
+ duplicateCmdlets = listOfCmdlets.Where(cmdlet => pkg.Includes.Cmdlet.Contains(cmdlet)).ToList();
+ }
+
+ if (pkg.Includes.Command != null && pkg.Includes.Command.Any())
+ {
+ duplicateCmds = listOfCmdlets.Where(commands => pkg.Includes.Command.Contains(commands, StringComparer.InvariantCultureIgnoreCase)).ToList();
+ }
+
+ if (duplicateCmdlets.Any() || duplicateCmds.Any())
+ {
+ duplicateCmdlets.AddRange(duplicateCmds);
+
+ var errMessage = string.Format(
+ "{1} package could not be installed with error: The following commands are already available on this system: '{0}'. This module '{1}' may override the existing commands. If you still want to install this module '{1}', remove the -NoClobber parameter.",
+ String.Join(", ", duplicateCmdlets), pkgName);
+
+ var ex = new ArgumentException(errMessage);
+ var noClobberError = new ErrorRecord(ex, "CommandAlreadyExists", ErrorCategory.ResourceExists, null);
+ error = noClobberError;
+ foundClobber = true;
+
+ return foundClobber;
+ }
+ }
+
+ return foundClobber;
+ }
+
+ ///
+ /// Creates metadata XML file for either module or script package.
+ ///
+ private bool CreateMetadataXMLFile(string dirNameVersion, string installPath, PSResourceInfo pkg, bool isModule, out ErrorRecord error)
+ {
+ error = null;
+ bool success = true;
+ // Script will have a metadata file similar to: "TestScript_InstalledScriptInfo.xml"
+ // Modules will have the metadata file: "PSGetModuleInfo.xml"
+ var metadataXMLPath = isModule ? Path.Combine(dirNameVersion, "PSGetModuleInfo.xml")
+ : Path.Combine(dirNameVersion, (pkg.Name + "_InstalledScriptInfo.xml"));
+
+ pkg.InstalledDate = DateTime.Now;
+ pkg.InstalledLocation = installPath;
+
+ // Write all metadata into metadataXMLPath
+ if (!pkg.TryWrite(metadataXMLPath, out string writeError))
+ {
+ var message = string.Format("{0} package could not be installed with error: Error parsing metadata into XML: '{1}'", pkg.Name, writeError);
+ var ex = new ArgumentException(message);
+ var errorParsingMetadata = new ErrorRecord(ex, "ErrorParsingMetadata", ErrorCategory.ParserError, null);
+ error = errorParsingMetadata;
+ success = false;
+ }
+
+ return success;
+ }
+
+ ///
+ /// Clean up and delete extraneous files found from the package during install.
+ ///
+ private void DeleteExtraneousFiles(string packageName, string dirNameVersion)
+ {
+ // Deleting .nupkg SHA file, .nuspec, and .nupkg after unpacking the module
+ // since we download as .zip for HTTP calls, we shouldn't have .nupkg* files
+ // var nupkgSHAToDelete = Path.Combine(dirNameVersion, pkgIdString + ".nupkg.sha512");
+ // var nupkgToDelete = Path.Combine(dirNameVersion, pkgIdString + ".nupkg");
+ // var nupkgMetadataToDelete = Path.Combine(dirNameVersion, ".nupkg.metadata");
+ var nuspecToDelete = Path.Combine(dirNameVersion, packageName + ".nuspec");
+ var contentTypesToDelete = Path.Combine(dirNameVersion, "[Content_Types].xml");
+ var relsDirToDelete = Path.Combine(dirNameVersion, "_rels");
+ var packageDirToDelete = Path.Combine(dirNameVersion, "package");
+
+ if (File.Exists(nuspecToDelete))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting '{0}'", nuspecToDelete));
+ File.Delete(nuspecToDelete);
+ }
+ if (File.Exists(contentTypesToDelete))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting '{0}'", contentTypesToDelete));
+ File.Delete(contentTypesToDelete);
+ }
+ if (Directory.Exists(relsDirToDelete))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting '{0}'", relsDirToDelete));
+ Utils.DeleteDirectory(relsDirToDelete);
+ }
+ if (Directory.Exists(packageDirToDelete))
+ {
+ _cmdletPassedIn.WriteVerbose(string.Format("Deleting '{0}'", packageDirToDelete));
+ Utils.DeleteDirectory(packageDirToDelete);
+ }
+ }
+
+ #endregion
+
+ #region Private NuGet API Methods
+
+ ///
+ /// Install provided list of packages, which include Dependent packages if requested (used for Local repositories)
///
private List InstallPackage(
List pkgsToInstall,
string repoName,
string repoUri,
PSCredentialInfo repoCredentialInfo,
- PSCredential credential,
bool isLocalRepo,
ScopeType scope)
{
@@ -389,7 +1387,7 @@ private List InstallPackage(
var ex = new ArgumentException(message);
var packageIdentityVersionParseError = new ErrorRecord(ex, "psdataFileNotExistError", ErrorCategory.ReadError, null);
_cmdletPassedIn.WriteError(packageIdentityVersionParseError);
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
continue;
}
@@ -412,75 +1410,38 @@ private List InstallPackage(
// Create the package extraction context
PackageExtractionContext packageExtractionContext = new PackageExtractionContext(
- packageSaveMode: PackageSaveMode.Nupkg,
- xmlDocFileSaveMode: PackageExtractionBehavior.XmlDocFileSaveMode,
- clientPolicyContext: null,
- logger: NullLogger.Instance);
-
- // Extracting from .nupkg and placing files into tempInstallPath
- result.PackageReader.CopyFiles(
- destination: tempInstallPath,
- packageFiles: result.PackageReader.GetFiles(),
- extractFile: new PackageFileExtractor(
- result.PackageReader.GetFiles(),
- packageExtractionContext.XmlDocFileSaveMode).ExtractPackageFile,
- logger: NullLogger.Instance,
- token: _cancellationToken);
- result.Dispose();
- }
- else
- {
- /* Download from a non-local repository */
- // Set up NuGet API resource for download
- PackageSource source = new PackageSource(repoUri);
+ packageSaveMode: PackageSaveMode.Nupkg,
+ xmlDocFileSaveMode: PackageExtractionBehavior.XmlDocFileSaveMode,
+ clientPolicyContext: null,
+ logger: NullLogger.Instance);
- // Explicitly passed in Credential takes precedence over repository CredentialInfo
- if (credential != null)
- {
- string password = new NetworkCredential(string.Empty, credential.Password).Password;
- source.Credentials = PackageSourceCredential.FromUserInput(repoUri, credential.UserName, password, true, null);
- }
- else if (repoCredentialInfo != null)
+ if (_asNupkg)
{
- PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
- repoName,
- repoCredentialInfo,
- _cmdletPassedIn);
-
- string password = new NetworkCredential(string.Empty, repoCredential.Password).Password;
- source.Credentials = PackageSourceCredential.FromUserInput(repoUri, repoCredential.UserName, password, true, null);
+ _cmdletPassedIn.WriteWarning("Saving resource from local/file based repository with -AsNupkg is not yet implemented feature.");
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ continue;
}
- var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider);
- SourceRepository repository = new SourceRepository(source, provider);
-
- /* Download from a non-local repository -- ie server */
- var downloadResource = repository.GetResourceAsync().GetAwaiter().GetResult();
- DownloadResourceResult result = null;
- try
+ else
{
- result = downloadResource.GetDownloadResourceResultAsync(
- identity: pkgIdentity,
- downloadContext: new PackageDownloadContext(cacheContext),
- globalPackagesFolder: tempInstallPath,
+ // Extracting from .nupkg and placing files into tempInstallPath
+ result.PackageReader.CopyFiles(
+ destination: tempInstallPath,
+ packageFiles: result.PackageReader.GetFiles(),
+ extractFile: new PackageFileExtractor(
+ result.PackageReader.GetFiles(),
+ packageExtractionContext.XmlDocFileSaveMode).ExtractPackageFile,
logger: NullLogger.Instance,
- token: _cancellationToken).GetAwaiter().GetResult();
- }
- catch (Exception e)
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Error attempting download: '{0}'", e.Message));
- }
- finally
- {
- // Need to close the .nupkg
- if (result != null) result.Dispose();
+ token: _cancellationToken);
}
+ result.Dispose();
}
+
_cmdletPassedIn.WriteVerbose(string.Format("Successfully able to download package from source to: '{0}'", tempInstallPath));
// pkgIdentity.Version.Version gets the version without metadata or release labels.
string newVersion = pkgIdentity.Version.ToNormalizedString();
- string normalizedVersionNoPrerelease = newVersion;
+ string normalizedVersionNoPrerelease = newVersion; // 3.0.17-beta or 2.2.5
if (pkgIdentity.Version.IsPrerelease)
{
// eg: 2.0.2
@@ -543,7 +1504,7 @@ private List InstallPackage(
var ex = new ArgumentException(message);
var psdataFileDoesNotExistError = new ErrorRecord(ex, "psdataFileNotExistError", ErrorCategory.ReadError, null);
_cmdletPassedIn.WriteError(psdataFileDoesNotExistError);
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
continue;
}
@@ -569,14 +1530,18 @@ private List InstallPackage(
pkg.RepositorySourceLocation = repoUri;
// Accept License verification
- if (!_savePkg && !CallAcceptLicense(pkg, moduleManifest, tempInstallPath, newVersion))
+ if (!_savePkg && !CallAcceptLicense(pkg, moduleManifest, tempInstallPath, newVersion, out ErrorRecord licenseError))
{
+ _cmdletPassedIn.WriteError(licenseError);
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
continue;
}
// If NoClobber is specified, ensure command clobbering does not happen
- if (_noClobber && DetectClobber(pkg.Name, parsedMetadataHashtable))
+ if (_noClobber && DetectClobber(pkg.Name, parsedMetadataHashtable, out ErrorRecord clobberError))
{
+ _cmdletPassedIn.WriteError(clobberError);
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
continue;
}
}
@@ -595,6 +1560,7 @@ out string[] _
_cmdletPassedIn.WriteError(error);
}
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
continue;
}
@@ -612,7 +1578,12 @@ out string[] _
if (_includeXml)
{
- CreateMetadataXMLFile(tempDirNameVersion, installPath, pkg, isModule);
+ if (!CreateMetadataXMLFile(tempDirNameVersion, installPath, pkg, isModule, out ErrorRecord createMetadataError))
+ {
+ _cmdletPassedIn.WriteError(createMetadataError);
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ continue;
+ }
}
MoveFilesIntoInstallPath(
@@ -651,7 +1622,7 @@ out string[] _
"InstallPackageFailed",
ErrorCategory.InvalidOperation,
_cmdletPassedIn));
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
+ _pkgNamesToInstall.RemoveAll(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
}
finally
{
@@ -675,177 +1646,9 @@ out string[] _
return pkgsSuccessfullyInstalled;
}
- private bool CallAcceptLicense(PSResourceInfo p, string moduleManifest, string tempInstallPath, string newVersion)
- {
- var requireLicenseAcceptance = false;
- var success = true;
-
- if (File.Exists(moduleManifest))
- {
- using (StreamReader sr = new StreamReader(moduleManifest))
- {
- var text = sr.ReadToEnd();
-
- var pattern = "RequireLicenseAcceptance\\s*=\\s*\\$true";
- var patternToSkip1 = "#\\s*RequireLicenseAcceptance\\s*=\\s*\\$true";
- var patternToSkip2 = "\\*\\s*RequireLicenseAcceptance\\s*=\\s*\\$true";
-
- Regex rgx = new Regex(pattern);
- Regex rgxComment1 = new Regex(patternToSkip1);
- Regex rgxComment2 = new Regex(patternToSkip2);
- if (rgx.IsMatch(text) && !rgxComment1.IsMatch(text) && !rgxComment2.IsMatch(text))
- {
- requireLicenseAcceptance = true;
- }
- }
-
- // Licesnse agreement processing
- if (requireLicenseAcceptance)
- {
- // If module requires license acceptance and -AcceptLicense is not passed in, display prompt
- if (!_acceptLicense)
- {
- var PkgTempInstallPath = Path.Combine(tempInstallPath, p.Name, newVersion);
- var LicenseFilePath = Path.Combine(PkgTempInstallPath, "License.txt");
-
- if (!File.Exists(LicenseFilePath))
- {
- var exMessage = String.Format("{0} package could not be installed with error: License.txt not found. License.txt must be provided when user license acceptance is required.", p.Name);
- var ex = new ArgumentException(exMessage);
- var acceptLicenseError = new ErrorRecord(ex, "LicenseTxtNotFound", ErrorCategory.ObjectNotFound, null);
-
- _cmdletPassedIn.WriteError(acceptLicenseError);
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(p.Name, StringComparison.InvariantCultureIgnoreCase));
- success = false;
- }
-
- // Otherwise read LicenseFile
- string licenseText = System.IO.File.ReadAllText(LicenseFilePath);
- var acceptanceLicenseQuery = $"Do you accept the license terms for module '{p.Name}'.";
- var message = licenseText + "`r`n" + acceptanceLicenseQuery;
-
- var title = "License Acceptance";
- var yesToAll = false;
- var noToAll = false;
- var shouldContinueResult = _cmdletPassedIn.ShouldContinue(message, title, true, ref yesToAll, ref noToAll);
-
- if (shouldContinueResult || yesToAll)
- {
- _acceptLicense = true;
- }
- }
-
- // Check if user agreed to license terms, if they didn't then throw error, otherwise continue to install
- if (!_acceptLicense)
- {
- var message = String.Format("{0} package could not be installed with error: License Acceptance is required for module '{0}'. Please specify '-AcceptLicense' to perform this operation.", p.Name);
- var ex = new ArgumentException(message);
- var acceptLicenseError = new ErrorRecord(ex, "ForceAcceptLicense", ErrorCategory.InvalidArgument, null);
-
- _cmdletPassedIn.WriteError(acceptLicenseError);
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(p.Name, StringComparison.InvariantCultureIgnoreCase));
- success = false;
- }
- }
- }
-
- return success;
- }
-
- private bool DetectClobber(string pkgName, Hashtable parsedMetadataHashtable)
- {
- // Get installed modules, then get all possible paths
- bool foundClobber = false;
- GetHelper getHelper = new GetHelper(_cmdletPassedIn);
- // selectPrereleaseOnly is false because even if Prerelease is true we want to include both stable and prerelease, never select prerelease only.
- IEnumerable pkgsAlreadyInstalled = getHelper.GetPackagesFromPath(
- name: new string[] { "*" },
- versionRange: VersionRange.All,
- pathsToSearch: _pathsToSearch,
- selectPrereleaseOnly: false);
- // User parsed metadata hash.
- HashSet cmdletsToInstall = new HashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var cmdletName in parsedMetadataHashtable["CmdletsToExport"] as object[])
- {
- cmdletsToInstall.Add(cmdletName as string);
- }
-
- // Exit early if there's no cmdlets in the package to be installed.
- if (cmdletsToInstall.Count == 0)
- {
- return foundClobber;
- }
-
- foreach (var pkg in pkgsAlreadyInstalled)
- {
- // See if any of the cmdlets or commands in the pkg we're trying to install exist within a package that's already installed.
- if (pkg.Includes.Cmdlet != null && pkg.Includes.Cmdlet.Any())
- {
- foreach (string cmdlet in pkg.Includes.Cmdlet)
- {
- if (cmdletsToInstall.Contains(cmdlet))
- {
- foundClobber = true;
-
- break;
- }
- }
- }
-
- if (pkg.Includes.Command != null && pkg.Includes.Command.Any())
- {
- foreach (string command in pkg.Includes.Command)
- {
- if (cmdletsToInstall.Contains(command))
- {
- foundClobber = true;
-
- break;
- }
- }
- }
-
- if (foundClobber) {
- _cmdletPassedIn.WriteError(new ErrorRecord(
- new PSInvalidOperationException(
- string.Format("{0} package could not be installed with error: One or more of the package's commands are already available on this system. " +
- "This module '{0}' may override the existing commands. If you still want to install this module '{0}', remove the -NoClobber parameter.",
- pkgName)),
- "CommandAlreadyExists",
- ErrorCategory.ResourceExists,
- this));
-
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkgName, StringComparison.InvariantCultureIgnoreCase));
-
- return foundClobber;
- }
- }
-
- return foundClobber;
- }
-
- private void CreateMetadataXMLFile(string dirNameVersion, string installPath, PSResourceInfo pkg, bool isModule)
- {
- // Script will have a metadata file similar to: "TestScript_InstalledScriptInfo.xml"
- // Modules will have the metadata file: "PSGetModuleInfo.xml"
- var metadataXMLPath = isModule ? Path.Combine(dirNameVersion, "PSGetModuleInfo.xml")
- : Path.Combine(dirNameVersion, (pkg.Name + "_InstalledScriptInfo.xml"));
-
- pkg.InstalledDate = DateTime.Now;
- pkg.InstalledLocation = installPath;
-
- // Write all metadata into metadataXMLPath
- if (!pkg.TryWrite(metadataXMLPath, out string error))
- {
- var message = string.Format("{0} package could not be installed with error: Error parsing metadata into XML: '{1}'", pkg.Name, error);
- var ex = new ArgumentException(message);
- var ErrorParsingMetadata = new ErrorRecord(ex, "ErrorParsingMetadata", ErrorCategory.ParserError, null);
-
- _cmdletPassedIn.WriteError(ErrorParsingMetadata);
- _pkgNamesToInstall.RemoveWhere(x => x.Equals(pkg.Name, StringComparison.InvariantCultureIgnoreCase));
- }
- }
-
+ ///
+ /// Clean up and delete extraneous files found from the package during install (used for Local repositories).
+ ///
private void DeleteExtraneousFiles(PackageIdentity pkgIdentity, string dirNameVersion)
{
// Deleting .nupkg SHA file, .nuspec, and .nupkg after unpacking the module
@@ -896,104 +1699,6 @@ private void DeleteExtraneousFiles(PackageIdentity pkgIdentity, string dirNameVe
}
}
- private bool TryDeleteDirectory(
- string tempInstallPath,
- out ErrorRecord errorMsg)
- {
- errorMsg = null;
-
- try
- {
- Utils.DeleteDirectory(tempInstallPath);
- }
- catch (Exception e)
- {
- var TempDirCouldNotBeDeletedError = new ErrorRecord(e, "errorDeletingTempInstallPath", ErrorCategory.InvalidResult, null);
- errorMsg = TempDirCouldNotBeDeletedError;
- return false;
- }
-
- return true;
- }
-
- private void MoveFilesIntoInstallPath(
- PSResourceInfo pkgInfo,
- bool isModule,
- bool isLocalRepo,
- string dirNameVersion,
- string tempInstallPath,
- string installPath,
- string newVersion,
- string moduleManifestVersion,
- string scriptPath)
- {
- // Creating the proper installation path depending on whether pkg is a module or script
- var newPathParent = isModule ? Path.Combine(installPath, pkgInfo.Name) : installPath;
- var finalModuleVersionDir = isModule ? Path.Combine(installPath, pkgInfo.Name, moduleManifestVersion) : installPath;
-
- // If script, just move the files over, if module, move the version directory over
- var tempModuleVersionDir = (!isModule || isLocalRepo) ? dirNameVersion
- : Path.Combine(tempInstallPath, pkgInfo.Name.ToLower(), newVersion);
-
- _cmdletPassedIn.WriteVerbose(string.Format("Installation source path is: '{0}'", tempModuleVersionDir));
- _cmdletPassedIn.WriteVerbose(string.Format("Installation destination path is: '{0}'", finalModuleVersionDir));
-
- if (isModule)
- {
- // If new path does not exist
- if (!Directory.Exists(newPathParent))
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Attempting to move '{0}' to '{1}'", tempModuleVersionDir, finalModuleVersionDir));
- Directory.CreateDirectory(newPathParent);
- Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir);
- }
- else
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Temporary module version directory is: '{0}'", tempModuleVersionDir));
-
- if (Directory.Exists(finalModuleVersionDir))
- {
- // Delete the directory path before replacing it with the new module.
- // If deletion fails (usually due to binary file in use), then attempt restore so that the currently
- // installed module is not corrupted.
- _cmdletPassedIn.WriteVerbose(string.Format("Attempting to delete with restore on failure.'{0}'", finalModuleVersionDir));
- Utils.DeleteDirectoryWithRestore(finalModuleVersionDir);
- }
-
- _cmdletPassedIn.WriteVerbose(string.Format("Attempting to move '{0}' to '{1}'", tempModuleVersionDir, finalModuleVersionDir));
- Utils.MoveDirectory(tempModuleVersionDir, finalModuleVersionDir);
- }
- }
- else
- {
- if (!_savePkg)
- {
- // Need to delete old xml files because there can only be 1 per script
- var scriptXML = pkgInfo.Name + "_InstalledScriptInfo.xml";
- _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(installPath, "InstalledScriptInfos", scriptXML))));
- if (File.Exists(Path.Combine(installPath, "InstalledScriptInfos", scriptXML)))
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Deleting script metadata XML"));
- File.Delete(Path.Combine(installPath, "InstalledScriptInfos", scriptXML));
- }
-
- _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML)));
- Utils.MoveFiles(Path.Combine(dirNameVersion, scriptXML), Path.Combine(installPath, "InstalledScriptInfos", scriptXML));
-
- // Need to delete old script file, if that exists
- _cmdletPassedIn.WriteVerbose(string.Format("Checking if path '{0}' exists: ", File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt))));
- if (File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)))
- {
- _cmdletPassedIn.WriteVerbose(string.Format("Deleting script file"));
- File.Delete(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
- }
- }
-
- _cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)));
- Utils.MoveFiles(scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
- }
- }
-
#endregion
}
}
diff --git a/src/code/InstallPSResource.cs b/src/code/InstallPSResource.cs
index 84e563235..d6bec1ea6 100644
--- a/src/code/InstallPSResource.cs
+++ b/src/code/InstallPSResource.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT License.
using Microsoft.PowerShell.PowerShellGet.UtilClasses;
-using Newtonsoft.Json;
using NuGet.Versioning;
using System;
using System.Collections;
@@ -10,8 +9,7 @@
using System.IO;
using System.Linq;
using System.Management.Automation;
-using Microsoft.PowerShell.Commands;
-
+using System.Net;
using Dbg = System.Diagnostics.Debug;
namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
@@ -244,13 +242,14 @@ private enum ResourceFileType
private string _requiredResourceFile;
private string _requiredResourceJson;
private Hashtable _requiredResourceHash;
+ private HashSet _packagesOnMachine;
VersionRange _versionRange;
InstallHelper _installHelper;
ResourceFileType _resourceFileType;
#endregion
- #region Method overrides
+ #region Method Overrides
protected override void BeginProcessing()
{
@@ -259,8 +258,13 @@ protected override void BeginProcessing()
RepositorySettings.CheckRepositoryStore();
_pathsToInstallPkg = Utils.GetAllInstallationPaths(this, Scope);
+ List pathsToSearch = Utils.GetAllResourcePaths(this, Scope);
+ // Only need to find packages installed if -Reinstall is not passed in
+ _packagesOnMachine = Reinstall ? new HashSet(StringComparer.CurrentCultureIgnoreCase) : Utils.GetInstalledPackages(pathsToSearch, this);
+
+ var networkCred = Credential != null ? new NetworkCredential(Credential.UserName, Credential.Password) : null;
- _installHelper = new InstallHelper(cmdletPassedIn: this);
+ _installHelper = new InstallHelper(cmdletPassedIn: this, networkCredential: networkCred);
}
protected override void ProcessRecord()
@@ -433,7 +437,7 @@ protected override void ProcessRecord()
#endregion
- #region Methods
+ #region Private Methods
private void RequiredResourceHelper(Hashtable reqResourceHash)
{
@@ -501,7 +505,7 @@ private void RequiredResourceHelper(Hashtable reqResourceHash)
private void ProcessInstallHelper(string[] pkgNames, VersionRange pkgVersion, bool pkgPrerelease, string[] pkgRepository, PSCredential pkgCredential, InstallPkgParams reqResourceParams)
{
- var inputNameToInstall = Utils.ProcessNameWildcards(pkgNames, out string[] errorMsgs, out bool nameContainsWildcard);
+ var inputNameToInstall = Utils.ProcessNameWildcards(pkgNames, removeWildcardEntries:false, out string[] errorMsgs, out bool nameContainsWildcard);
if (nameContainsWildcard)
{
WriteError(new ErrorRecord(
@@ -537,6 +541,7 @@ private void ProcessInstallHelper(string[] pkgNames, VersionRange pkgVersion, bo
var installedPkgs = _installHelper.InstallPackages(
names: pkgNames,
versionRange: pkgVersion,
+ versionString: Version,
prerelease: pkgPrerelease,
repository: pkgRepository,
acceptLicense: AcceptLicense,
@@ -545,7 +550,6 @@ private void ProcessInstallHelper(string[] pkgNames, VersionRange pkgVersion, bo
force: false,
trustRepository: TrustRepository,
noClobber: NoClobber,
- credential: pkgCredential,
asNupkg: false,
includeXml: true,
skipDependencyCheck: SkipDependencyCheck,
@@ -553,7 +557,8 @@ private void ProcessInstallHelper(string[] pkgNames, VersionRange pkgVersion, bo
savePkg: false,
pathsToInstallPkg: _pathsToInstallPkg,
scope: Scope,
- tmpPath: _tmpPath);
+ tmpPath: _tmpPath,
+ pkgsInstalled: _packagesOnMachine);
if (PassThru)
{
diff --git a/src/code/InstallPkgParams.cs b/src/code/InstallPkgParams.cs
index 20000a512..0b656d688 100644
--- a/src/code/InstallPkgParams.cs
+++ b/src/code/InstallPkgParams.cs
@@ -4,7 +4,6 @@
using Microsoft.PowerShell.PowerShellGet.UtilClasses;
using NuGet.Versioning;
using System;
-using System.Collections;
using System.Management.Automation;
public class InstallPkgParams
@@ -101,5 +100,6 @@ public void SetProperty(string propertyName, string propertyValue, out ErrorReco
break;
}
}
+
#endregion
}
\ No newline at end of file
diff --git a/src/code/PSGetException.cs b/src/code/PSGetException.cs
new file mode 100644
index 000000000..c714d1ac9
--- /dev/null
+++ b/src/code/PSGetException.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ public class OperationNotSupportedException : Exception
+ {
+ public OperationNotSupportedException(string message)
+ : base(message)
+ {
+ }
+
+ }
+
+ public class V3ResourceNotFoundException : Exception
+ {
+ public V3ResourceNotFoundException(string message)
+ : base (message)
+ {
+ }
+ }
+
+ public class JsonParsingException : Exception
+ {
+ public JsonParsingException(string message)
+ : base (message)
+ {
+ }
+ }
+
+ public class SpecifiedTagsNotFoundException : Exception
+ {
+ public SpecifiedTagsNotFoundException(string message)
+ : base (message)
+ {
+ }
+ }
+
+ public class InvalidOrEmptyResponse : Exception
+ {
+ public InvalidOrEmptyResponse(string message)
+ : base (message)
+ {
+ }
+ }
+}
diff --git a/src/code/PSRepositoryInfo.cs b/src/code/PSRepositoryInfo.cs
index 39b42aadd..226d50e3d 100644
--- a/src/code/PSRepositoryInfo.cs
+++ b/src/code/PSRepositoryInfo.cs
@@ -11,15 +11,28 @@ namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
///
public sealed class PSRepositoryInfo
{
+ #region Enums
+
+ public enum APIVersion
+ {
+ Unknown,
+ v2,
+ v3,
+ local
+ }
+
+ #endregion
+
#region Constructor
- public PSRepositoryInfo(string name, Uri uri, int priority, bool trusted, PSCredentialInfo credentialInfo)
+ public PSRepositoryInfo(string name, Uri uri, int priority, bool trusted, PSCredentialInfo credentialInfo, APIVersion apiVersion)
{
Name = name;
Uri = uri;
Priority = priority;
Trusted = trusted;
CredentialInfo = credentialInfo;
+ ApiVersion = apiVersion;
}
#endregion
@@ -52,6 +65,11 @@ public PSRepositoryInfo(string name, Uri uri, int priority, bool trusted, PSCred
///
public PSCredentialInfo CredentialInfo { get; }
+ ///
+ /// the API protocol version for the repository
+ ///
+ public APIVersion ApiVersion { get; }
+
#endregion
}
}
diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs
index ca6057002..6af95f99b 100644
--- a/src/code/PSResourceInfo.cs
+++ b/src/code/PSResourceInfo.cs
@@ -9,6 +9,8 @@
using System.Globalization;
using System.Linq;
using System.Management.Automation;
+using System.Text.Json;
+using System.Xml;
using Dbg = System.Diagnostics.Debug;
@@ -16,24 +18,28 @@ namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
{
#region Enums
- [Flags]
public enum ResourceType
{
- None = 0x0,
- Module = 0x1,
- Script = 0x2,
- Command = 0x4,
- DscResource = 0x8
+ None,
+ Module,
+ Script
}
public enum VersionType
{
- Unknown,
- MinimumVersion,
- RequiredVersion,
- MaximumVersion
+ NoVersion,
+ SpecificVersion,
+ VersionRange
}
+ // public enum VersionType
+ // {
+ // Unknown,
+ // MinimumVersion,
+ // RequiredVersion,
+ // MaximumVersion
+ // }
+
public enum ScopeType
{
CurrentUser,
@@ -44,21 +50,21 @@ public enum ScopeType
#region VersionInfo
- public sealed class VersionInfo
- {
- public VersionInfo(
- VersionType versionType,
- Version versionNum)
- {
- VersionType = versionType;
- VersionNum = versionNum;
- }
+ // public sealed class VersionInfo
+ // {
+ // public VersionInfo(
+ // VersionType versionType,
+ // Version versionNum)
+ // {
+ // VersionType = versionType;
+ // VersionNum = versionNum;
+ // }
- public VersionType VersionType { get; }
- public Version VersionNum { get; }
+ // public VersionType VersionType { get; }
+ // public Version VersionNum { get; }
- public override string ToString() => $"{VersionType}: {VersionNum}";
- }
+ // public override string ToString() => $"{VersionType}: {VersionNum}";
+ // }
#endregion
@@ -181,9 +187,8 @@ public sealed class Dependency
///
/// Constructor
- ///
+ /// An object describes a package dependency
///
- /// Hashtable of PSGet includes
public Dependency(string dependencyName, VersionRange dependencyVersionRange)
{
Name = dependencyName;
@@ -202,7 +207,7 @@ public sealed class PSCommandResourceInfo
// included by the PSResourceInfo property
#region Properties
- public string Name { get; }
+ public string[] Names { get; }
public PSResourceInfo ParentResource { get; }
@@ -213,11 +218,11 @@ public sealed class PSCommandResourceInfo
///
/// Constructor
///
- /// Name of the command or DSC resource
+ /// Name of the command or DSC resource
/// the parent module resource the command or dsc resource belongs to
- public PSCommandResourceInfo(string name, PSResourceInfo parentResource)
+ public PSCommandResourceInfo(string[] names, PSResourceInfo parentResource)
{
- Name = name;
+ Names = names;
ParentResource = parentResource;
}
@@ -233,27 +238,27 @@ public sealed class PSResourceInfo
#region Properties
public Dictionary AdditionalMetadata { get; }
- public string Author { get; }
- public string CompanyName { get; internal set; }
- public string Copyright { get; internal set; }
- public Dependency[] Dependencies { get; }
- public string Description { get; }
- public Uri IconUri { get; }
+ public string Author { get; set; }
+ public string CompanyName { get; set; }
+ public string Copyright { get; set; }
+ public Dependency[] Dependencies { get; set; }
+ public string Description { get; set; }
+ public Uri IconUri { get; set; }
public ResourceIncludes Includes { get; }
- public DateTime? InstalledDate { get; internal set; }
- public string InstalledLocation { get; internal set; }
- public bool IsPrerelease { get; }
- public Uri LicenseUri { get; }
- public string Name { get; }
+ public DateTime? InstalledDate { get; set; }
+ public string InstalledLocation { get; set; }
+ public bool IsPrerelease { get; set; }
+ public Uri LicenseUri { get; set; }
+ public string Name { get; set; }
public string PackageManagementProvider { get; }
public string PowerShellGetFormatVersion { get; }
public string Prerelease { get; }
- public Uri ProjectUri { get; }
- public DateTime? PublishedDate { get; }
- public string ReleaseNotes { get; internal set; }
- public string Repository { get; }
- public string RepositorySourceLocation { get; internal set; }
- public string[] Tags { get; }
+ public Uri ProjectUri { get; set; }
+ public DateTime? PublishedDate { get; set; }
+ public string ReleaseNotes { get; set; }
+ public string Repository { get; set; }
+ public string RepositorySourceLocation { get; set; }
+ public string[] Tags { get; set; }
public ResourceType Type { get; }
public DateTime? UpdatedDate { get; }
public Version Version { get; }
@@ -535,7 +540,7 @@ public static bool TryConvert(
licenseUri: ParseMetadataLicenseUri(metadataToParse),
name: ParseMetadataName(metadataToParse),
packageManagementProvider: null,
- powershellGetFormatVersion: null,
+ powershellGetFormatVersion: null,
prerelease: ParsePrerelease(metadataToParse),
projectUri: ParseMetadataProjectUri(metadataToParse),
publishedDate: ParseMetadataPublishedDate(metadataToParse),
@@ -560,6 +565,290 @@ public static bool TryConvert(
}
}
+ ///
+ /// Converts XML entry to PSResourceInfo instance
+ /// used for V2 Server API call find response conversion to PSResourceInfo object
+ ///
+ public static bool TryConvertFromXml(
+ XmlNode entry,
+ out PSResourceInfo psGetInfo,
+ string repositoryName,
+ out string errorMsg)
+ {
+ psGetInfo = null;
+ errorMsg = String.Empty;
+
+ if (entry == null)
+ {
+ errorMsg = "TryConvertXmlToPSResourceInfo: Invalid XmlNodeList object. Object cannot be null.";
+ return false;
+ }
+
+ try
+ {
+ Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
+
+ var childNodes = entry.ChildNodes;
+ foreach (XmlElement child in childNodes)
+ {
+ var key = child.LocalName;
+ var value = child.InnerText;
+
+ if (key.Equals("Version"))
+ {
+ metadata[key] = ParseHttpVersion(value, out string prereleaseLabel);
+ metadata["Prerelease"] = prereleaseLabel;
+ }
+ else if (key.EndsWith("Url"))
+ {
+ metadata[key] = ParseHttpUrl(value) as Uri;
+ }
+ else if (key.Equals("Tags"))
+ {
+ metadata[key] = value.Split(new char[]{' '});
+ }
+ else if (key.Equals("Published"))
+ {
+ metadata[key] = ParseHttpDateTime(value);
+ }
+ else if (key.Equals("Dependencies"))
+ {
+ metadata[key] = ParseHttpDependencies(value);
+ }
+ else if (key.Equals("IsPrerelease"))
+ {
+ bool.TryParse(value, out bool isPrerelease);
+
+ metadata[key] = isPrerelease;
+ }
+ else if (key.Equals("NormalizedVersion"))
+ {
+ if (!NuGetVersion.TryParse(value, out NuGetVersion parsedNormalizedVersion))
+ {
+ errorMsg = string.Format(
+ CultureInfo.InvariantCulture,
+ @"TryReadPSGetInfo: Cannot parse NormalizedVersion");
+
+ parsedNormalizedVersion = new NuGetVersion("1.0.0.0");
+ }
+
+ metadata[key] = parsedNormalizedVersion;
+ }
+ else
+ {
+ metadata[key] = value;
+ }
+ }
+
+ var typeInfo = ParseHttpMetadataType(metadata["Tags"] as string[], out ArrayList commandNames, out ArrayList dscResourceNames);
+ var resourceHashtable = new Hashtable();
+ resourceHashtable.Add(nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames));
+ resourceHashtable.Add(nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames));
+
+ var additionalMetadataHashtable = new Dictionary();
+ additionalMetadataHashtable.Add("NormalizedVersion", metadata["NormalizedVersion"].ToString());
+
+ var includes = new ResourceIncludes(resourceHashtable);
+
+ psGetInfo = new PSResourceInfo(
+ additionalMetadata: additionalMetadataHashtable,
+ author: metadata["Authors"] as String,
+ companyName: metadata["CompanyName"] as String,
+ copyright: metadata["Copyright"] as String,
+ dependencies: metadata["Dependencies"] as Dependency[],
+ description: metadata["Description"] as String,
+ iconUri: metadata["IconUrl"] as Uri,
+ includes: includes,
+ installedDate: null,
+ installedLocation: null,
+ isPrelease: (bool) metadata["IsPrerelease"],
+ licenseUri: metadata["LicenseUrl"] as Uri,
+ name: metadata["Id"] as String,
+ packageManagementProvider: null,
+ powershellGetFormatVersion: null,
+ prerelease: metadata["Prerelease"] as String,
+ projectUri: metadata["ProjectUrl"] as Uri,
+ publishedDate: metadata["Published"] as DateTime?,
+ releaseNotes: metadata["ReleaseNotes"] as String,
+ repository: repositoryName,
+ repositorySourceLocation: null,
+ tags: metadata["Tags"] as string[],
+ type: typeInfo,
+ updatedDate: null,
+ version: metadata["Version"] as Version);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ errorMsg = string.Format(
+ CultureInfo.InvariantCulture,
+ @"TryConvertFromXml: Cannot parse PSResourceInfo from XmlNode with error: {0}",
+ ex.Message);
+ return false;
+ }
+ }
+
+
+ ///
+ /// Converts JsonDocument entry to PSResourceInfo instance
+ /// used for V3 Server API call find response conversion to PSResourceInfo object
+ ///
+ public static bool TryConvertFromJson(
+ JsonDocument pkgJson,
+ out PSResourceInfo psGetInfo,
+ string repositoryName,
+ out string errorMsg)
+ {
+ psGetInfo = null;
+ errorMsg = String.Empty;
+
+ if (pkgJson == null)
+ {
+ errorMsg = "TryConvertJsonToPSResourceInfo: Invalid json object. Object cannot be null.";
+ return false;
+ }
+
+ try
+ {
+ Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
+ JsonElement rootDom = pkgJson.RootElement;
+
+ // Version
+ if (rootDom.TryGetProperty("version", out JsonElement versionElement))
+ {
+ string versionValue = versionElement.ToString();
+ metadata["Version"] = ParseHttpVersion(versionValue, out string prereleaseLabel);
+ metadata["Prerelease"] = prereleaseLabel;
+
+ if (!NuGetVersion.TryParse(versionValue, out NuGetVersion parsedNormalizedVersion))
+ {
+ errorMsg = string.Format(
+ CultureInfo.InvariantCulture,
+ @"TryReadPSGetInfo: Cannot parse NormalizedVersion");
+
+ parsedNormalizedVersion = new NuGetVersion("1.0.0.0");
+ }
+ metadata["NormalizedVersion"] = parsedNormalizedVersion;
+ }
+
+ // License Url
+ if (rootDom.TryGetProperty("licenseUrl", out JsonElement licenseUrlElement))
+ {
+ metadata["LicenseUrl"] = ParseHttpUrl(licenseUrlElement.ToString()) as Uri;
+ }
+
+ // Project Url
+ if (rootDom.TryGetProperty("projectUrl", out JsonElement projectUrlElement))
+ {
+ metadata["ProjectUrl"] = ParseHttpUrl(projectUrlElement.ToString()) as Uri;
+ }
+
+ // Tags
+ if (rootDom.TryGetProperty("tags", out JsonElement tagsElement))
+ {
+ List tags = new List();
+ foreach (var tag in tagsElement.EnumerateArray())
+ {
+ tags.Add(tag.ToString());
+ }
+ metadata["Tags"] = tags.ToArray();
+ }
+
+ // PublishedDate
+ if (rootDom.TryGetProperty("published", out JsonElement publishedElement))
+ {
+ metadata["PublishedDate"] = ParseHttpDateTime(publishedElement.ToString());
+ }
+
+ // Dependencies
+ // TODO 3.0.0-beta21, a little complicated
+
+ // IsPrerelease
+ if (rootDom.TryGetProperty("isPrerelease", out JsonElement isPrereleaseElement))
+ {
+ metadata["IsPrerelease"] = isPrereleaseElement.GetBoolean();
+ }
+
+ // Author
+ if (rootDom.TryGetProperty("authors", out JsonElement authorsElement))
+ {
+ metadata["Authors"] = authorsElement.ToString();
+
+ // CompanyName
+ // CompanyName is not provided in v3 pkg metadata response, so we've just set it to the author,
+ // which is often the company
+ metadata["CompanyName"] = authorsElement.ToString();
+ }
+
+ // Copyright
+ if (rootDom.TryGetProperty("copyright", out JsonElement copyrightElement))
+ {
+ metadata["Copyright"] = copyrightElement.ToString();
+ }
+
+ // Description
+ if (rootDom.TryGetProperty("description", out JsonElement descriptiontElement))
+ {
+ metadata["Description"] = descriptiontElement.ToString();
+ }
+
+ // Id
+ if (rootDom.TryGetProperty("id", out JsonElement idElement))
+ {
+ metadata["Id"] = idElement.ToString();
+ }
+
+ // ReleaseNotes
+ if (rootDom.TryGetProperty("releaseNotes", out JsonElement releaseNotesElement)) {
+ metadata["ReleaseNotes"] = releaseNotesElement.ToString();
+ }
+
+ var additionalMetadataHashtable = new Dictionary
+ {
+ { "NormalizedVersion", metadata["NormalizedVersion"].ToString() }
+ };
+
+ psGetInfo = new PSResourceInfo(
+ additionalMetadata: additionalMetadataHashtable,
+ author: metadata["Authors"] as String,
+ companyName: metadata["CompanyName"] as String,
+ copyright: metadata["Copyright"] as String,
+ dependencies: metadata["Dependencies"] as Dependency[],
+ description: metadata["Description"] as String,
+ iconUri: null,
+ includes: null,
+ installedDate: null,
+ installedLocation: null,
+ isPrelease: (bool)metadata["IsPrerelease"],
+ licenseUri: metadata["LicenseUrl"] as Uri,
+ name: metadata["Id"] as String,
+ packageManagementProvider: null,
+ powershellGetFormatVersion: null,
+ prerelease: metadata["Prerelease"] as String,
+ projectUri: metadata["ProjectUrl"] as Uri,
+ publishedDate: metadata["PublishedDate"] as DateTime?,
+ releaseNotes: metadata["ReleaseNotes"] as String,
+ repository: repositoryName,
+ repositorySourceLocation: null,
+ tags: metadata["Tags"] as string[],
+ type: ResourceType.None,
+ updatedDate: null,
+ version: metadata["Version"] as Version);
+
+ return true;
+
+ }
+ catch (Exception ex)
+ {
+ errorMsg = string.Format(
+ CultureInfo.InvariantCulture,
+ @"TryConvertFromJson: Cannot parse PSResourceInfo from json object with error: {0}",
+ ex.Message);
+ return false;
+ }
+ }
+
#endregion
#region Private static methods
@@ -723,6 +1012,8 @@ private static string ConcatenateVersionWithPrerelease(string version, string pr
return Utils.GetNormalizedVersionString(version, prerelease);
}
+ #endregion
+
#region Parse Metadata private static methods
private static string ParseMetadataAuthor(IPackageSearchMetadata pkg)
@@ -853,13 +1144,11 @@ private static ResourceType ParseMetadataType(
if (tag.StartsWith("PSCommand_", StringComparison.InvariantCultureIgnoreCase))
{
- currentPkgType |= ResourceType.Command;
commandNames.Add(tag.Split('_')[1]);
}
if (tag.StartsWith("PSDscResource_", StringComparison.InvariantCultureIgnoreCase))
{
- currentPkgType |= ResourceType.DscResource;
dscResourceNames.Add(tag.Split('_')[1]);
}
}
@@ -877,7 +1166,137 @@ private static Version ParseMetadataVersion(IPackageSearchMetadata pkg)
return null;
}
- #endregion
+ private static Version ParseHttpVersion(string versionString, out string prereleaseLabel)
+ {
+ prereleaseLabel = String.Empty;
+
+ if (!String.IsNullOrEmpty(versionString))
+ {
+ string pkgVersion = versionString;
+ if (versionString.Contains("-"))
+ {
+ // versionString: "1.2.0-alpha1"
+ string[] versionStringParsed = versionString.Split('-');
+ if (versionStringParsed.Length == 1)
+ {
+ // versionString: "1.2.0-" (unlikely, at least should not be from our PSResourceInfo.TryWrite())
+ pkgVersion = versionStringParsed[0];
+ }
+ else
+ {
+ // versionStringParsed.Length > 1 (because string contained '-' so couldn't be 0)
+ // versionString: "1.2.0-alpha1"
+ pkgVersion = versionStringParsed[0];
+ prereleaseLabel = versionStringParsed[1];
+ }
+ }
+
+ // at this point, version is normalized (i.e either "1.2.0" (if part of prerelease) or "1.2.0.0" otherwise)
+ // parse the pkgVersion parsed out above into a System.Version object
+ if (!Version.TryParse(pkgVersion, out Version parsedVersion))
+ {
+ prereleaseLabel = String.Empty;
+ return null;
+ }
+ else
+ {
+ return parsedVersion;
+ }
+ }
+
+ // version could not be parsed as string, it was written to XML file as a System.Version object
+ // V3 code briefly did so, I believe so we provide support for it
+ return new System.Version();
+ }
+
+ public static Uri ParseHttpUrl(string uriString)
+ {
+ Uri parsedUri;
+ Uri.TryCreate(uriString, UriKind.Absolute, out parsedUri);
+
+ return parsedUri;
+ }
+
+ public static DateTime? ParseHttpDateTime(string publishedString)
+ {
+ DateTime.TryParse(publishedString, out DateTime parsedDateTime);
+ return parsedDateTime;
+ }
+
+ public static Dependency[] ParseHttpDependencies(string dependencyString)
+ {
+ /*
+ Az.Profile:[0.1.0, ):|Az.Aks:[0.1.0, ):|Az.AnalysisServices:[0.1.0, ):
+ Post 1st Split:
+ ["Az.Profile:[0.1.0, ):", "Az.Aks:[0.1.0, ):", "Az.AnalysisServices:[0.1.0, ):"]
+ */
+ string[] dependencies = dependencyString.Split(new char[]{'|'}, StringSplitOptions.RemoveEmptyEntries);
+
+ List dependencyList = new List();
+ foreach (string dependency in dependencies)
+ {
+ /*
+ The Element: "Az.Profile:[0.1.0, ):"
+ Post 2nd Split: ["Az.Profile", "[0.1.0, )"]
+ */
+ string[] dependencyParts = dependency.Split(new char[]{':'}, StringSplitOptions.RemoveEmptyEntries);
+
+ VersionRange dependencyVersion;
+ if (dependencyParts.Length == 1)
+ {
+ dependencyVersion = VersionRange.All;
+ }
+ else
+ {
+ if (!Utils.TryParseVersionOrVersionRange(dependencyParts[1], out dependencyVersion))
+ {
+ dependencyVersion = VersionRange.All;
+ }
+ }
+
+ dependencyList.Add(new Dependency(dependencyParts[0], dependencyVersion));
+ }
+
+ return dependencyList.ToArray();
+ }
+
+ private static ResourceType ParseHttpMetadataType(
+ string[] tags,
+ out ArrayList commandNames,
+ out ArrayList dscResourceNames)
+ {
+ // possible type combinations:
+ // M, C
+ // M, D
+ // M
+ // S
+
+ commandNames = new ArrayList();
+ dscResourceNames = new ArrayList();
+
+ ResourceType pkgType = ResourceType.Module;
+ foreach (string tag in tags)
+ {
+ if(String.Equals(tag, "PSScript", StringComparison.InvariantCultureIgnoreCase))
+ {
+ // clear default Module tag, because a Script resource cannot be a Module resource also
+ pkgType = ResourceType.Script;
+ pkgType &= ~ResourceType.Module;
+ }
+
+ if (tag.StartsWith("PSCommand_", StringComparison.InvariantCultureIgnoreCase))
+ {
+ commandNames.Add(tag.Split('_')[1]);
+ }
+
+ if (tag.StartsWith("PSDscResource_", StringComparison.InvariantCultureIgnoreCase))
+ {
+ dscResourceNames.Add(tag.Split('_')[1]);
+ }
+ }
+
+ return pkgType;
+ }
#endregion
diff --git a/src/code/PSResourceResult.cs b/src/code/PSResourceResult.cs
new file mode 100644
index 000000000..20526f153
--- /dev/null
+++ b/src/code/PSResourceResult.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ public sealed class PSResourceResult
+ {
+ internal PSResourceInfo returnedObject { get; set; }
+ internal PSCommandResourceInfo returnedCmdObject { get; set; }
+ internal string errorMsg { get; set; }
+ internal bool isTerminatingError { get; set; }
+
+
+ public PSResourceResult(PSResourceInfo returnedObject, string errorMsg, bool isTerminatingError)
+ {
+ this.returnedObject = returnedObject;
+ this.errorMsg = errorMsg;
+ this.isTerminatingError = isTerminatingError;
+ }
+
+
+ public PSResourceResult(PSCommandResourceInfo returnedCmdObject, string errorMsg, bool isTerminatingError)
+ {
+ this.returnedCmdObject = returnedCmdObject;
+ this.errorMsg = errorMsg;
+ this.isTerminatingError = isTerminatingError;
+ }
+ }
+}
diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs
index da80e196b..c7b9e076d 100644
--- a/src/code/PSScriptFileInfo.cs
+++ b/src/code/PSScriptFileInfo.cs
@@ -1,8 +1,8 @@
-using System.Collections;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
diff --git a/src/code/PowerShellGet.csproj b/src/code/PowerShellGet.csproj
index 664024242..c1c06162f 100644
--- a/src/code/PowerShellGet.csproj
+++ b/src/code/PowerShellGet.csproj
@@ -9,7 +9,7 @@
3.0.19
3.0.19
netstandard2.0
- 8.0
+ 9.0
@@ -25,6 +25,7 @@
+
diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs
index 4b9277792..937146d97 100644
--- a/src/code/PublishPSResource.cs
+++ b/src/code/PublishPSResource.cs
@@ -17,6 +17,7 @@
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Language;
+using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
@@ -132,7 +133,7 @@ public PSCredential ProxyCredential {
private string pathToModuleManifestToPublish = string.Empty;
private string pathToModuleDirToPublish = string.Empty;
private ResourceType resourceType = ResourceType.None;
-
+ private NetworkCredential _networkCredential;
#endregion
#region Method overrides
@@ -141,6 +142,8 @@ protected override void BeginProcessing()
{
_cancellationToken = new CancellationToken();
+ _networkCredential = Credential != null ? new NetworkCredential(Credential.UserName, Credential.Password) : null;
+
// Create a respository story (the PSResourceRepository.xml file) if it does not already exist
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
RepositorySettings.CheckRepositoryStore();
@@ -754,23 +757,43 @@ private bool CheckDependenciesExist(Hashtable dependencies, string repositoryNam
depVersion = string.IsNullOrWhiteSpace(depVersion) ? "*" : depVersion;
VersionRange versionRange = null;
- if (!Utils.TryParseVersionOrVersionRange(depVersion, out versionRange))
+ VersionType versionType = VersionType.VersionRange;
+ NuGetVersion nugetVersion = null;
+
+ if (depVersion != null)
{
- // This should never be true because Test-ModuleManifest will throw an error if dependency versions are incorrectly formatted
- // This is being left as a safeguard for parsing a version from a string to a version range.
- ThrowTerminatingError(new ErrorRecord(
- new ArgumentException(string.Format("Error parsing dependency version {0}, from the module {1}", depVersion, depName)),
- "IncorrectVersionFormat",
- ErrorCategory.InvalidArgument,
- this));
+ if (!NuGetVersion.TryParse(depVersion, out nugetVersion))
+ {
+ if (depVersion.Trim().Equals("*"))
+ {
+ versionRange = VersionRange.All;
+ versionType = VersionType.VersionRange;
+ }
+ else if (!VersionRange.TryParse(depVersion, out versionRange))
+ {
+ ThrowTerminatingError(new ErrorRecord(
+ new ArgumentException("Argument for -Version parameter is not in the proper format"),
+ "IncorrectVersionFormat",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+ }
+ else
+ {
+ versionType = VersionType.SpecificVersion;
+ }
+ }
+ else
+ {
+ versionType = VersionType.NoVersion;
}
// Search for and return the dependency if it's in the repository.
- FindHelper findHelper = new FindHelper(_cancellationToken, this);
+ FindHelper findHelper = new FindHelper(_cancellationToken, this, _networkCredential);
bool depPrerelease = depVersion.Contains("-");
var repository = new[] { repositoryName };
- var dependencyFound = findHelper.FindByResourceName(depName, ResourceType.Module, depVersion, depPrerelease, null, repository, Credential, false);
+ var dependencyFound = findHelper.FindByResourceName(depName, ResourceType.Module, versionRange, nugetVersion, versionType, depVersion, depPrerelease, null, repository, false);
if (dependencyFound == null || !dependencyFound.Any())
{
var message = String.Format("Dependency '{0}' was not found in repository '{1}'. Make sure the dependency is published to the repository before publishing this module.", dependency, repositoryName);
diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs
index e5a7cd16b..e79d76fc1 100644
--- a/src/code/RepositorySettings.cs
+++ b/src/code/RepositorySettings.cs
@@ -9,6 +9,7 @@
using System.Management.Automation;
using System.Xml;
using System.Xml.Linq;
+using System.Xml.XPath;
namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
{
@@ -60,7 +61,7 @@ public static void CheckRepositoryStore()
// Add PSGallery to the newly created store
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
- Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, force: false);
+ Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, PSRepositoryInfo.APIVersion.v2, force: false);
}
// Open file (which should exist now), if cannot/is corrupted then throw error
@@ -103,6 +104,8 @@ public static PSRepositoryInfo AddToRepositoryStore(string repoName, Uri repoUri
return null;
}
+ PSRepositoryInfo.APIVersion apiVersion = GetRepoAPIVersion(repoUri);
+
if (repoCredentialInfo != null)
{
bool isSecretManagementModuleAvailable = Utils.IsSecretManagementModuleAvailable(repoName, cmdletPassedIn);
@@ -136,7 +139,7 @@ public static PSRepositoryInfo AddToRepositoryStore(string repoName, Uri repoUri
return null;
}
- var repo = RepositorySettings.Add(repoName, repoUri, repoPriority, repoTrusted, repoCredentialInfo, force);
+ var repo = RepositorySettings.Add(repoName, repoUri, repoPriority, repoTrusted, repoCredentialInfo, apiVersion, force);
return repo;
}
@@ -226,7 +229,7 @@ public static PSRepositoryInfo UpdateRepositoryStore(string repoName, Uri repoUr
/// Returns: PSRepositoryInfo containing information about the repository just added to the repository store
///
///
- public static PSRepositoryInfo Add(string repoName, Uri repoUri, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo, bool force)
+ public static PSRepositoryInfo Add(string repoName, Uri repoUri, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo, PSRepositoryInfo.APIVersion apiVersion, bool force)
{
try
{
@@ -261,6 +264,7 @@ public static PSRepositoryInfo Add(string repoName, Uri repoUri, int repoPriorit
"Repository",
new XAttribute("Name", repoName),
new XAttribute("Url", repoUri),
+ new XAttribute("APIVersion", apiVersion),
new XAttribute("Priority", repoPriority),
new XAttribute("Trusted", repoTrusted)
);
@@ -281,7 +285,7 @@ public static PSRepositoryInfo Add(string repoName, Uri repoUri, int repoPriorit
throw new PSInvalidOperationException(String.Format("Adding to repository store failed: {0}", e.Message));
}
- return new PSRepositoryInfo(repoName, repoUri, repoPriority, repoTrusted, repoCredentialInfo);
+ return new PSRepositoryInfo(repoName, repoUri, repoPriority, repoTrusted, repoCredentialInfo, apiVersion);
}
///
@@ -317,7 +321,13 @@ public static PSRepositoryInfo Update(string repoName, Uri repoUri, int repoPrio
{
errorMsg = $"Repository element does not contain neccessary 'Trusted' attribute, in file located at path: {FullRepositoryPath}. Fix this in your file and run again.";
return null;
- }
+ }
+
+ if (node.Attribute("APIVersion") == null)
+ {
+ errorMsg = $"Repository element does not contain neccessary 'APIVersion' attribute, in file located at path: {FullRepositoryPath}. Fix this in your file and run again.";
+ return null;
+ }
bool urlAttributeExists = node.Attribute("Url") != null;
bool uriAttributeExists = node.Attribute("Uri") != null;
@@ -336,6 +346,7 @@ public static PSRepositoryInfo Update(string repoName, Uri repoUri, int repoPrio
// determine if existing repository node (which we wish to update) had Url or Uri attribute
Uri thisUrl = null;
+ PSRepositoryInfo.APIVersion apiVersion = (PSRepositoryInfo.APIVersion)Enum.Parse(typeof(PSRepositoryInfo.APIVersion), node.Attribute("APIVersion").Value);
if (repoUri != null)
{
if (!Uri.TryCreate(repoUri.AbsoluteUri, UriKind.Absolute, out thisUrl))
@@ -351,6 +362,8 @@ public static PSRepositoryInfo Update(string repoName, Uri repoUri, int repoPrio
{
node.Attribute("Uri").Value = thisUrl.AbsoluteUri;
}
+
+ apiVersion = GetRepoAPIVersion(repoUri);
}
else
{
@@ -421,7 +434,8 @@ public static PSRepositoryInfo Update(string repoName, Uri repoUri, int repoPrio
thisUrl,
Int32.Parse(node.Attribute("Priority").Value),
Boolean.Parse(node.Attribute("Trusted").Value),
- thisCredentialInfo);
+ thisCredentialInfo,
+ apiVersion);
// Close the file
root.Save(FullRepositoryPath);
@@ -484,6 +498,12 @@ public static List Remove(string[] repoNames, out string[] err
continue;
}
+ if (node.Attribute("APIVersion") == null)
+ {
+ tempErrorList.Add(String.Format("Repository element does not contain neccessary 'APIVersion' attribute, in file located at path: {0}. Fix this in your file and run again.", FullRepositoryPath));
+ continue;
+ }
+
// determine if repo had Url or Uri (less likely) attribute
bool urlAttributeExists = node.Attribute("Url") != null;
bool uriAttributeExists = node.Attribute("Uri") != null;
@@ -499,7 +519,9 @@ public static List Remove(string[] repoNames, out string[] err
new Uri(node.Attribute(attributeUrlUriName).Value),
Int32.Parse(node.Attribute("Priority").Value),
Boolean.Parse(node.Attribute("Trusted").Value),
- repoCredentialInfo));
+ repoCredentialInfo,
+ (PSRepositoryInfo.APIVersion)Enum.Parse(typeof(PSRepositoryInfo.APIVersion), node.Attribute("APIVersion").Value)));
+
// Remove item from file
node.Remove();
}
@@ -570,6 +592,7 @@ public static List Read(string[] repoNames, out string[] error
tempErrorList.Add(String.Format("Unable to read incorrectly formatted Url for repo {0}", repo.Attribute("Name").Value));
continue;
}
+
}
else if (uriAttributeExists)
{
@@ -580,6 +603,15 @@ public static List Read(string[] repoNames, out string[] error
}
}
+ if (repo.Attribute("APIVersion") == null)
+ {
+ PSRepositoryInfo.APIVersion apiVersion = GetRepoAPIVersion(thisUrl);
+
+ XElement repoXElem = FindRepositoryElement(doc, repo.Attribute("Name").Value);
+ repoXElem.SetAttributeValue("APIVersion", apiVersion.ToString());
+ doc.Save(FullRepositoryPath);
+ }
+
PSCredentialInfo thisCredentialInfo;
string credentialInfoErrorMessage = $"Repository {repo.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty";
// both keys are present
@@ -616,7 +648,8 @@ public static List Read(string[] repoNames, out string[] error
thisUrl,
Int32.Parse(repo.Attribute("Priority").Value),
Boolean.Parse(repo.Attribute("Trusted").Value),
- thisCredentialInfo);
+ thisCredentialInfo,
+ (PSRepositoryInfo.APIVersion)Enum.Parse(typeof(PSRepositoryInfo.APIVersion), repo.Attribute("APIVersion").Value));
foundRepos.Add(currentRepoItem);
}
@@ -673,6 +706,15 @@ public static List Read(string[] repoNames, out string[] error
}
}
+ if (node.Attribute("APIVersion") == null)
+ {
+ PSRepositoryInfo.APIVersion apiVersion = GetRepoAPIVersion(thisUrl);
+
+ XElement repoXElem = FindRepositoryElement(doc, node.Attribute("Name").Value);
+ repoXElem.SetAttributeValue("APIVersion", apiVersion.ToString());
+ doc.Save(FullRepositoryPath);
+ }
+
PSCredentialInfo thisCredentialInfo;
string credentialInfoErrorMessage = $"Repository {node.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty";
// both keys are present
@@ -709,7 +751,8 @@ public static List Read(string[] repoNames, out string[] error
thisUrl,
Int32.Parse(node.Attribute("Priority").Value),
Boolean.Parse(node.Attribute("Trusted").Value),
- thisCredentialInfo);
+ thisCredentialInfo,
+ (PSRepositoryInfo.APIVersion)Enum.Parse(typeof(PSRepositoryInfo.APIVersion), node.Attribute("APIVersion").Value));
foundRepos.Add(currentRepoItem);
}
@@ -761,6 +804,26 @@ private static XDocument LoadXDocument(string filePath)
return XDocument.Load(xmlReader);
}
+ private static PSRepositoryInfo.APIVersion GetRepoAPIVersion(Uri repoUri) {
+
+ if (repoUri.AbsoluteUri.EndsWith("api/v2"))
+ {
+ return PSRepositoryInfo.APIVersion.v2;
+ }
+ else if (repoUri.AbsoluteUri.EndsWith("v3/index.json"))
+ {
+ return PSRepositoryInfo.APIVersion.v3;
+ }
+ else if (repoUri.Scheme == Uri.UriSchemeFile)
+ {
+ return PSRepositoryInfo.APIVersion.local;
+ }
+ else
+ {
+ return PSRepositoryInfo.APIVersion.Unknown;
+ }
+ }
+
#endregion
}
}
diff --git a/src/code/ResponseUtil.cs b/src/code/ResponseUtil.cs
new file mode 100644
index 000000000..be162dc05
--- /dev/null
+++ b/src/code/ResponseUtil.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using System.Collections.Generic;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal abstract class ResponseUtil
+ {
+ #region Members
+
+ public abstract PSRepositoryInfo repository { get; set; }
+
+ #endregion
+
+ #region Constructor
+
+ public ResponseUtil(PSRepositoryInfo repository)
+ {
+ this.repository = repository;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public abstract IEnumerable ConvertToPSResourceResult(string[] responses);
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/src/code/ResponseUtilFactory.cs b/src/code/ResponseUtilFactory.cs
new file mode 100644
index 000000000..92baed6f5
--- /dev/null
+++ b/src/code/ResponseUtilFactory.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal class ResponseUtilFactory
+ {
+ public static ResponseUtil GetResponseUtil(PSRepositoryInfo repository)
+ {
+ PSRepositoryInfo.APIVersion repoApiVersion = repository.ApiVersion;
+ ResponseUtil currentResponseUtil = null;
+
+ switch (repoApiVersion)
+ {
+ case PSRepositoryInfo.APIVersion.v2:
+ currentResponseUtil = new V2ResponseUtil(repository);
+ break;
+
+ case PSRepositoryInfo.APIVersion.v3:
+ currentResponseUtil = new V3ResponseUtil(repository);
+ break;
+ }
+
+ return currentResponseUtil;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/code/SavePSResource.cs b/src/code/SavePSResource.cs
index 14193eaa3..9c28c6205 100644
--- a/src/code/SavePSResource.cs
+++ b/src/code/SavePSResource.cs
@@ -8,7 +8,7 @@
using System.IO;
using System.Linq;
using System.Management.Automation;
-
+using System.Net;
using Dbg = System.Diagnostics.Debug;
namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
@@ -179,7 +179,9 @@ protected override void BeginProcessing()
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
RepositorySettings.CheckRepositoryStore();
- _installHelper = new InstallHelper(cmdletPassedIn: this);
+ var networkCred = Credential != null ? new NetworkCredential(Credential.UserName, Credential.Password) : null;
+
+ _installHelper = new InstallHelper(cmdletPassedIn: this, networkCredential: networkCred);
}
protected override void ProcessRecord()
@@ -237,7 +239,7 @@ protected override void ProcessRecord()
private void ProcessSaveHelper(string[] pkgNames, bool pkgPrerelease, string[] pkgRepository)
{
- var namesToSave = Utils.ProcessNameWildcards(pkgNames, out string[] errorMsgs, out bool nameContainsWildcard);
+ var namesToSave = Utils.ProcessNameWildcards(pkgNames, removeWildcardEntries:false, out string[] errorMsgs, out bool nameContainsWildcard);
if (nameContainsWildcard)
{
WriteError(new ErrorRecord(
@@ -271,25 +273,26 @@ private void ProcessSaveHelper(string[] pkgNames, bool pkgPrerelease, string[] p
}
var installedPkgs = _installHelper.InstallPackages(
- names: namesToSave,
- versionRange: _versionRange,
- prerelease: pkgPrerelease,
- repository: pkgRepository,
- acceptLicense: true,
- quiet: Quiet,
- reinstall: true,
- force: false,
+ names: namesToSave,
+ versionRange: _versionRange,
+ versionString: Version,
+ prerelease: pkgPrerelease,
+ repository: pkgRepository,
+ acceptLicense: true,
+ quiet: Quiet,
+ reinstall: true,
+ force: false,
trustRepository: TrustRepository,
- credential: Credential,
- noClobber: false,
- asNupkg: AsNupkg,
- includeXml: IncludeXml,
+ noClobber: false,
+ asNupkg: AsNupkg,
+ includeXml: IncludeXml,
skipDependencyCheck: SkipDependencyCheck,
authenticodeCheck: AuthenticodeCheck,
savePkg: true,
pathsToInstallPkg: new List { _path },
scope: null,
- tmpPath: _tmpPath);
+ tmpPath: _tmpPath,
+ pkgsInstalled: new HashSet(StringComparer.InvariantCultureIgnoreCase));
if (PassThru)
{
diff --git a/src/code/ServerApiCall.cs b/src/code/ServerApiCall.cs
new file mode 100644
index 000000000..31c141df5
--- /dev/null
+++ b/src/code/ServerApiCall.cs
@@ -0,0 +1,128 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using NuGet.Versioning;
+using System.Net;
+using System.Runtime.ExceptionServices;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal abstract class ServerApiCall : IServerAPICalls
+ {
+ #region Members
+
+ public abstract PSRepositoryInfo repository { get; set; }
+ public abstract HttpClient s_client { get; set; }
+
+ #endregion
+
+ #region Constructor
+
+ public ServerApiCall(PSRepositoryInfo repository, NetworkCredential networkCredential)
+ {
+ this.repository = repository;
+ HttpClientHandler handler = new HttpClientHandler()
+ {
+ Credentials = networkCredential
+ };
+
+ s_client = new HttpClient(handler);
+ }
+
+ #endregion
+
+ #region Methods
+ // High level design: Find-PSResource >>> IFindPSResource (loops, version checks, etc.) >>> IServerAPICalls (call to repository endpoint/url)
+
+ ///
+ /// Find method which allows for searching for all packages from a repository and returns latest version for each.
+ ///
+ public abstract string[] FindAll(bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for packages with tag from a repository and returns latest version for each.
+ /// Examples: Search -Tag "JSON" -Repository PSGallery
+ /// API call:
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsAbsoluteLatestVersion&searchTerm=tag:JSON&includePrerelease=true
+ ///
+ public abstract string[] FindTag(string tag, bool includePrerelease, ResourceType _type, out ExceptionDispatchInfo edi);
+
+ public abstract string[] FindCommandOrDscResource(string tag, bool includePrerelease, bool isSearchingForCommands, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name and returns latest version.
+ /// Name: no wildcard support
+ /// Examples: Search "PowerShellGet"
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference)
+ ///
+ public abstract string FindName(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ public abstract string FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name with wildcards and returns latest version.
+ /// Name: supports wildcards
+ /// Examples: Search "PowerShell*"
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsLatestVersion&searchTerm='az*'
+ /// Implementation Note: filter additionally and verify ONLY package name was a match.
+ ///
+ public abstract string[] FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+
+ public abstract string[] FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi);
+ ///
+ /// Find method which allows for searching for single name with version range.
+ /// Name: no wildcard support
+ /// Version: supports wildcards
+ /// Examples: Search "PowerShellGet" "[3.0.0.0, 5.0.0.0]"
+ /// Search "PowerShellGet" "3.*"
+ /// API Call: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation note: Returns all versions, including prerelease ones. Later (in the API client side) we'll do filtering on the versions to satisfy what user provided.
+ ///
+ public abstract string[] FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, out ExceptionDispatchInfo edi);
+
+ ///
+ /// Find method which allows for searching for single name with specific version.
+ /// Name: no wildcard support
+ /// Version: no wildcard support
+ /// Examples: Search "PowerShellGet" "2.2.5"
+ /// API call: http://www.powershellgallery.com/api/v2/Packages(Id='PowerShellGet', Version='2.2.5')
+ ///
+ public abstract string FindVersion(string packageName, string version, ResourceType type, out ExceptionDispatchInfo edi);
+
+ public abstract string FindVersionWithTag(string packageName, string version, string[] tags, ResourceType type, out ExceptionDispatchInfo edi);
+
+ /** INSTALL APIS **/
+
+ ///
+ /// Installs specific package.
+ /// Name: no wildcard support.
+ /// Examples: Install "PowerShellGet"
+ /// Implementation Note: if not prerelease: https://www.powershellgallery.com/api/v2/package/powershellget (Returns latest stable)
+ /// if prerelease, the calling method should first call IFindPSResource.FindName(),
+ /// then find the exact version to install, then call into install version
+ ///
+ public abstract HttpContent InstallName(string packageName, bool includePrerelease, out ExceptionDispatchInfo edi);
+
+
+ ///
+ /// Installs package with specific name and version.
+ /// Name: no wildcard support.
+ /// Version: no wildcard support.
+ /// Examples: Install "PowerShellGet" -Version "3.0.0.0"
+ /// Install "PowerShellGet" -Version "3.0.0-beta16"
+ /// API Call: https://www.powershellgallery.com/api/v2/package/Id/version (version can be prerelease)
+ ///
+ public abstract HttpContent InstallVersion(string packageName, string version, out ExceptionDispatchInfo edi);
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/src/code/ServerFactory.cs b/src/code/ServerFactory.cs
new file mode 100644
index 000000000..119bad2d6
--- /dev/null
+++ b/src/code/ServerFactory.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using System.Net;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal class ServerFactory
+ {
+ public static ServerApiCall GetServer(PSRepositoryInfo repository, NetworkCredential networkCredential)
+ {
+ PSRepositoryInfo.APIVersion repoApiVersion = repository.ApiVersion;
+ ServerApiCall currentServer = null;
+
+ switch (repoApiVersion)
+ {
+ case PSRepositoryInfo.APIVersion.v2:
+ currentServer = new V2ServerAPICalls(repository, networkCredential);
+ break;
+
+ case PSRepositoryInfo.APIVersion.v3:
+ currentServer = new V3ServerAPICalls(repository, networkCredential);
+ break;
+ }
+
+ return currentServer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/code/UninstallPSResource.cs b/src/code/UninstallPSResource.cs
index c2fad5d08..971aac6e7 100644
--- a/src/code/UninstallPSResource.cs
+++ b/src/code/UninstallPSResource.cs
@@ -101,7 +101,7 @@ protected override void ProcessRecord()
ThrowTerminatingError(IncorrectVersionFormat);
}
- Name = Utils.ProcessNameWildcards(Name, out string[] errorMsgs, out bool _);
+ Name = Utils.ProcessNameWildcards(Name, removeWildcardEntries:false, out string[] errorMsgs, out bool _);
foreach (string error in errorMsgs)
{
diff --git a/src/code/UnregisterPSResourceRepository.cs b/src/code/UnregisterPSResourceRepository.cs
index c18257007..058393c1d 100644
--- a/src/code/UnregisterPSResourceRepository.cs
+++ b/src/code/UnregisterPSResourceRepository.cs
@@ -45,7 +45,7 @@ protected override void BeginProcessing()
}
protected override void ProcessRecord()
{
- Name = Utils.ProcessNameWildcards(Name, out string[] _, out bool nameContainsWildcard);
+ Name = Utils.ProcessNameWildcards(Name, removeWildcardEntries:false, out string[] _, out bool nameContainsWildcard);
if (nameContainsWildcard)
{
var message = String.Format("Name: '{0}, cannot contain wildcards", String.Join(", ", Name));
diff --git a/src/code/UpdatePSResource.cs b/src/code/UpdatePSResource.cs
index 63bc7ea07..0dd2846cf 100644
--- a/src/code/UpdatePSResource.cs
+++ b/src/code/UpdatePSResource.cs
@@ -5,9 +5,9 @@
using NuGet.Versioning;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Management.Automation;
+using System.Net;
using System.Threading;
namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
@@ -155,35 +155,54 @@ protected override void BeginProcessing()
RepositorySettings.CheckRepositoryStore();
_pathsToInstallPkg = Utils.GetAllInstallationPaths(this, Scope);
-
_cancellationTokenSource = new CancellationTokenSource();
+ var networkCred = Credential != null ? new NetworkCredential(Credential.UserName, Credential.Password) : null;
+
_findHelper = new FindHelper(
cancellationToken: _cancellationTokenSource.Token,
- cmdletPassedIn: this);
+ cmdletPassedIn: this,
+ networkCredential: networkCred);
- _installHelper = new InstallHelper(cmdletPassedIn: this);
+ _installHelper = new InstallHelper(cmdletPassedIn: this, networkCredential: networkCred);
}
protected override void ProcessRecord()
{
- VersionRange versionRange;
+ // determine/parse out Version param
+ VersionType versionType = VersionType.VersionRange;
+ NuGetVersion nugetVersion = null;
+ VersionRange versionRange = null;
- // handle case where Version == null
- if (Version == null) {
- versionRange = VersionRange.All;
+ if (Version != null)
+ {
+ if (!NuGetVersion.TryParse(Version, out nugetVersion))
+ {
+ if (Version.Trim().Equals("*"))
+ {
+ versionRange = VersionRange.All;
+ versionType = VersionType.VersionRange;
+ }
+ else if (!VersionRange.TryParse(Version, out versionRange))
+ {
+ WriteError(new ErrorRecord(
+ new ArgumentException("Argument for -Version parameter is not in the proper format"),
+ "IncorrectVersionFormat",
+ ErrorCategory.InvalidArgument,
+ this));
+ return;
+ }
+ }
+ else
+ {
+ versionType = VersionType.SpecificVersion;
+ }
}
- else if (!Utils.TryParseVersionOrVersionRange(Version, out versionRange))
+ else
{
- // Only returns false if the range was incorrectly formatted and couldn't be parsed.
- WriteError(new ErrorRecord(
- new PSInvalidOperationException("Cannot parse Version parameter provided into VersionRange"),
- "ErrorParsingVersionParamIntoVersionRange",
- ErrorCategory.InvalidArgument,
- this));
- return;
+ versionType = VersionType.NoVersion;
}
- var namesToUpdate = ProcessPackageNames(Name, versionRange);
+ var namesToUpdate = ProcessPackageNames(Name, versionRange, nugetVersion, versionType);
if (namesToUpdate.Length == 0)
{
@@ -199,6 +218,7 @@ protected override void ProcessRecord()
var installedPkgs = _installHelper.InstallPackages(
names: namesToUpdate,
versionRange: versionRange,
+ versionString: Version,
prerelease: Prerelease,
repository: Repository,
acceptLicense: AcceptLicense,
@@ -206,7 +226,6 @@ protected override void ProcessRecord()
reinstall: true,
force: Force,
trustRepository: TrustRepository,
- credential: Credential,
noClobber: false,
asNupkg: false,
includeXml: true,
@@ -215,7 +234,8 @@ protected override void ProcessRecord()
savePkg: false,
pathsToInstallPkg: _pathsToInstallPkg,
scope: Scope,
- tmpPath: _tmpPath);
+ tmpPath: _tmpPath,
+ pkgsInstalled: new HashSet(StringComparer.InvariantCultureIgnoreCase));
if (PassThru)
{
@@ -252,10 +272,13 @@ protected override void EndProcessing()
///
private string[] ProcessPackageNames(
string[] namesToProcess,
- VersionRange versionRange)
+ VersionRange versionRange,
+ NuGetVersion nuGetVersion,
+ VersionType versionType)
{
namesToProcess = Utils.ProcessNameWildcards(
pkgNames: namesToProcess,
+ removeWildcardEntries:false,
errorMsgs: out string[] errorMsgs,
isContainWildcard: out bool _);
@@ -315,11 +338,13 @@ private string[] ProcessPackageNames(
foreach (var foundResource in _findHelper.FindByResourceName(
name: installedPackages.Keys.ToArray(),
type: ResourceType.None,
+ versionRange: versionRange,
+ nugetVersion: nuGetVersion,
+ versionType: versionType,
version: Version,
prerelease: Prerelease,
tag: null,
repository: Repository,
- credential: Credential,
includeDependencies: !SkipDependencyCheck))
{
if (!repositoryPackages.ContainsKey(foundResource.Name))
@@ -362,22 +387,14 @@ private string[] ProcessPackageNames(
continue;
}
- if (!NuGetVersion.TryParse(repositoryPackage.Version.ToString(), out NuGetVersion repositoryPackageNuGetVersion))
- {
- WriteWarning($"Cannot parse nuget version in repository package '{repositoryPackage.Name}'. Cannot update package.");
- continue;
- }
-
- // We compare NuGetVersions instead of System.Version as repositoryPackage.Version (3.0.17.0) and installedPackage.Version (3.0.17.-1)
- // should refer to the same version but with System.Version end up having discrepancies which yields incorrect results.
- if ((versionRange == VersionRange.All && repositoryPackageNuGetVersion > installedVersion) ||
- !versionRange.Satisfies(installedVersion))
+ if (((versionRange == null || versionRange == VersionRange.All) && repositoryPackage.Version > installedPackage.Version) ||
+ (versionRange != null && !versionRange.Satisfies(installedVersion)))
{
namesToUpdate.Add(repositoryPackage.Name);
}
else
{
- WriteVerbose($"Installed package {repositoryPackage.Name} {repositoryPackageNuGetVersion} is already up to date.");
+ WriteVerbose($"Installed package {repositoryPackage.Name} {repositoryPackage.Version} is already up to date.");
}
}
diff --git a/src/code/Utils.cs b/src/code/Utils.cs
index 77ad2d313..90f2723a1 100644
--- a/src/code/Utils.cs
+++ b/src/code/Utils.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-using Microsoft.Win32.SafeHandles;
using NuGet.Versioning;
using System;
using System.Collections;
@@ -9,14 +8,12 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
-using System.Net;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Runtime.InteropServices;
-using System.Security;
-using System.Security.Cryptography.X509Certificates;
using Microsoft.PowerShell.Commands;
+using Microsoft.PowerShell.PowerShellGet.Cmdlets;
namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
{
@@ -127,6 +124,7 @@ public static string[] GetStringArray(ArrayList list)
public static string[] ProcessNameWildcards(
string[] pkgNames,
+ bool removeWildcardEntries,
out string[] errorMsgs,
out bool isContainWildcard)
{
@@ -145,6 +143,13 @@ public static string[] ProcessNameWildcards(
{
if (WildcardPattern.ContainsWildcardCharacters(name))
{
+ if (removeWildcardEntries)
+ {
+ // Tag // CommandName // DSCResourceName
+ errorMsgsList.Add($"{name} will be discarded from the provided entries.");
+ continue;
+ }
+
if (String.Equals(name, "*", StringComparison.InvariantCultureIgnoreCase))
{
isContainWildcard = true;
@@ -234,7 +239,6 @@ public static bool TryParseVersionOrVersionRange(
return true;
}
- // parse as Version range
return VersionRange.TryParse(version, out versionRange);
}
@@ -267,11 +271,10 @@ public static bool GetVersionForInstallPath(
return false;
}
- string version = psGetInfo.Version.ToString();
- string prerelease = psGetInfo.Prerelease;
+ psGetInfo.AdditionalMetadata.TryGetValue("NormalizedVersion", out string normalizedVersion);
if (!NuGetVersion.TryParse(
- value: String.IsNullOrEmpty(prerelease) ? version : GetNormalizedVersionString(version, prerelease),
+ value: normalizedVersion,
version: out pkgNuGetVersion))
{
cmdletPassedIn.WriteVerbose(String.Format("Leaf directory in path '{0}' cannot be parsed into a version.", installedPkgPath));
@@ -732,6 +735,45 @@ private static void GetStandardPlatformPaths(
}
}
+ ///
+ /// Checks if any of the package versions are already installed and if they are removes them from the list of packages to install.
+ ///
+ internal static HashSet GetInstalledPackages(List pathsToSearch, PSCmdlet cmdletPassedIn)
+ {
+ // Package install paths.
+ // _pathsToInstallPkg will only contain the paths specified within the -Scope param (if applicable).
+ // _pathsToSearch will contain all resource package subdirectories within _pathsToInstallPkg path locations.
+ // e.g.:
+ // ./InstallPackagePath1/PackageA
+ // ./InstallPackagePath1/PackageB
+ // ./InstallPackagePath2/PackageC
+ // ./InstallPackagePath3/PackageD
+
+ // Get currently installed packages.
+ var getHelper = new GetHelper(cmdletPassedIn);
+ var pkgsInstalledOnMachine = new HashSet(StringComparer.CurrentCultureIgnoreCase);
+
+ foreach (PSResourceInfo installedPkg in getHelper.GetPackagesFromPath(
+ name: new string[] { "*" },
+ versionRange: VersionRange.All,
+ pathsToSearch: pathsToSearch,
+ selectPrereleaseOnly: false))
+ {
+ string pkgNameVersion = CreateHashSetKey(installedPkg.Name, installedPkg.Version.ToString());
+ if (!pkgsInstalledOnMachine.Contains(pkgNameVersion))
+ {
+ pkgsInstalledOnMachine.Add(pkgNameVersion);
+ }
+ }
+
+ return pkgsInstalledOnMachine;
+ }
+
+ internal static string CreateHashSetKey(string packageName, string packageVersion)
+ {
+ return $"{packageName}{packageVersion}";
+ }
+
#endregion
#region PSDataFile parsing
@@ -1186,6 +1228,7 @@ private static void RestoreDirContents(
}
#endregion
+
}
#endregion
@@ -1364,6 +1407,7 @@ internal static bool CheckAuthenticodeSignature(
return true;
}
+ /*
// First check if the files are catalog signed.
string catalogFilePath = Path.Combine(tempDirNameVersion, pkgName + ".cat");
if (File.Exists(catalogFilePath))
@@ -1413,6 +1457,7 @@ internal static bool CheckAuthenticodeSignature(
return true;
}
+ */
// Otherwise check for signatures on individual files.
Collection authenticodeSignatures;
diff --git a/src/code/V2ResponseUtil.cs b/src/code/V2ResponseUtil.cs
new file mode 100644
index 000000000..f32d9c2c8
--- /dev/null
+++ b/src/code/V2ResponseUtil.cs
@@ -0,0 +1,84 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Xml;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal class V2ResponseUtil : ResponseUtil
+ {
+ #region Members
+
+ public override PSRepositoryInfo repository { get; set; }
+
+ #endregion
+
+ #region Constructor
+
+ public V2ResponseUtil(PSRepositoryInfo repository) : base(repository)
+ {
+ this.repository = repository;
+ }
+
+ #endregion
+
+ #region Overriden Methods
+ public override IEnumerable ConvertToPSResourceResult(string[] responses)
+ {
+ // in FindHelper:
+ // serverApi.FindName() -> return responses, and out errRecord
+ // check outErrorRecord
+ //
+ // v2Converter.ConvertToPSResourceInfo(responses) -> return PSResourceResult
+ // check resourceResult for error, write if needed
+
+ foreach (string response in responses)
+ {
+ var elemList = ConvertResponseToXML(response);
+ if (elemList.Length == 0)
+ {
+ // this indicates we got a non-empty, XML response (as noticed for V2 server) but it's not a response that's meaningful (contains 'properties')
+ string errorMsg = $"Response didn't contain properties element";
+ yield return new PSResourceResult(returnedObject: null, errorMsg: errorMsg, isTerminatingError: false);
+ }
+
+ foreach (var element in elemList)
+ {
+ if (!PSResourceInfo.TryConvertFromXml(element, out PSResourceInfo psGetInfo, repository.Name, out string errorMsg))
+ {
+ yield return new PSResourceResult(returnedObject: null, errorMsg: errorMsg, isTerminatingError: false);
+ }
+
+ yield return new PSResourceResult(returnedObject: psGetInfo, errorMsg: String.Empty, isTerminatingError: false);
+ }
+ }
+ }
+
+ #endregion
+
+ #region V2 Specific Methods
+
+ public XmlNode[] ConvertResponseToXML(string httpResponse) {
+
+ //Create the XmlDocument.
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(httpResponse);
+
+ XmlNodeList elemList = doc.GetElementsByTagName("m:properties");
+
+ XmlNode[] nodes = new XmlNode[elemList.Count];
+ for (int i=0; i>> IFindPSResource (loops, version checks, etc.) >>> IServerAPICalls (call to repository endpoint/url)
+
+ ///
+ /// Find method which allows for searching for all packages from a repository and returns latest version for each.
+ /// Examples: Search -Repository PSGallery
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsLatestVersion
+ ///
+ public override string[] FindAll(bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi) {
+ edi = null;
+ List responses = new List();
+
+ if (type == ResourceType.Script || type == ResourceType.None)
+ {
+ int scriptSkip = 0;
+ string initialScriptResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: false, scriptSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialScriptResponse);
+ int initalScriptCount = GetCountFromResponse(initialScriptResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalScriptCount / 6000;
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ scriptSkip += 6000;
+ var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: false, scriptSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+ }
+ if (type != ResourceType.Script)
+ {
+ int moduleSkip = 0;
+ string initialModuleResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: true, moduleSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialModuleResponse);
+ int initalModuleCount = GetCountFromResponse(initialModuleResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalModuleCount / 6000;
+
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ moduleSkip += 6000;
+ var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: true, moduleSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+ }
+
+ return responses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for packages with tag from a repository and returns latest version for each.
+ /// Examples: Search -Tag "JSON" -Repository PSGallery
+ /// API call:
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsAbsoluteLatestVersion&searchTerm=tag:JSON&includePrerelease=true
+ ///
+ public override string[] FindTag(string tag, bool includePrerelease, ResourceType _type, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ List responses = new List();
+
+ if (_type == ResourceType.Script || _type == ResourceType.None)
+ {
+ int scriptSkip = 0;
+ string initialScriptResponse = FindTagFromEndpoint(tag, includePrerelease, isSearchingModule: false, scriptSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialScriptResponse);
+ int initalScriptCount = GetCountFromResponse(initialScriptResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalScriptCount / 100;
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ // skip 100
+ scriptSkip += 100;
+ var tmpResponse = FindTagFromEndpoint(tag, includePrerelease, isSearchingModule: false, scriptSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+ }
+ if (_type != ResourceType.Script)
+ {
+ int moduleSkip = 0;
+ string initialModuleResponse = FindTagFromEndpoint(tag, includePrerelease, isSearchingModule: true, moduleSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialModuleResponse);
+ int initalModuleCount = GetCountFromResponse(initialModuleResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalModuleCount / 100;
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ moduleSkip += 100;
+ var tmpResponse = FindTagFromEndpoint(tag, includePrerelease, isSearchingModule: true, moduleSkip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+ }
+
+ return responses.ToArray();
+ }
+
+ public override string[] FindCommandOrDscResource(string tag, bool includePrerelease, bool isSearchingForCommands, out ExceptionDispatchInfo edi)
+ {
+ List responses = new List();
+ int skip = 0;
+
+ string initialResponse = FindCommandOrDscResource(tag, includePrerelease, isSearchingForCommands, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialResponse);
+ int initialCount = GetCountFromResponse(initialResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initialCount / 100;
+
+ while (count > 0)
+ {
+ skip += 100;
+ var tmpResponse = FindCommandOrDscResource(tag, includePrerelease, isSearchingForCommands, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+
+ return responses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for single name and returns latest version.
+ /// Name: no wildcard support
+ /// Examples: Search "PowerShellGet"
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference)
+ ///
+ public override string FindName(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ // Make sure to include quotations around the package name
+ var prerelease = includePrerelease ? "IsAbsoluteLatestVersion" : "IsLatestVersion";
+
+ // This should return the latest stable version or the latest prerelease version (respectively)
+ // https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'&$filter=IsLatestVersion and substringof('PSModule', Tags) eq true
+ string typeFilterPart = type == ResourceType.None ? $" and Id eq '{packageName}'" : $" and substringof('PS{type.ToString()}', Tags) eq true";
+ var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'&$filter={prerelease}{typeFilterPart}&{select}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ public override string FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ // Make sure to include quotations around the package name
+ var prerelease = includePrerelease ? "IsAbsoluteLatestVersion" : "IsLatestVersion";
+
+ // This should return the latest stable version or the latest prerelease version (respectively)
+ // https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'&$filter=IsLatestVersion and substringof('PSModule', Tags) eq true
+ string typeFilterPart = type == ResourceType.None ? $" and Id eq '{packageName}'" : $" and substringof('PS{type.ToString()}', Tags) eq true";
+
+ string tagFilterPart = String.Empty;
+ foreach (string tag in tags)
+ {
+ tagFilterPart += $" and substringof('{tag}', Tags) eq true";
+ }
+
+ var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'&$filter={prerelease}{typeFilterPart}{tagFilterPart}&{select}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ ///
+ /// Find method which allows for searching for single name with wildcards and returns latest version.
+ /// Name: supports wildcards
+ /// Examples: Search "PowerShell*"
+ /// API call:
+ /// - No prerelease: http://www.powershellgallery.com/api/v2/Search()?$filter=IsLatestVersion&searchTerm='az*'
+ /// Implementation Note: filter additionally and verify ONLY package name was a match.
+ ///
+ public override string[] FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ List responses = new List();
+ int skip = 0;
+
+ var initialResponse = FindNameGlobbing(packageName, type, includePrerelease, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+
+ responses.Add(initialResponse);
+
+ // check count (regex) 425 ==> count/100 ~~> 4 calls
+ int initalCount = GetCountFromResponse(initialResponse, out edi); // count = 4
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalCount / 100;
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ // skip 100
+ skip += 100;
+ var tmpResponse = FindNameGlobbing(packageName, type, includePrerelease, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+
+ return responses.ToArray();
+ }
+
+ public override string[] FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ List responses = new List();
+ int skip = 0;
+
+ var initialResponse = FindNameGlobbingWithTag(packageName, tags, type, includePrerelease, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+
+ responses.Add(initialResponse);
+
+ // check count (regex) 425 ==> count/100 ~~> 4 calls
+ int initalCount = GetCountFromResponse(initialResponse, out edi); // count = 4
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalCount / 100;
+ // if more than 100 count, loop and add response to list
+ while (count > 0)
+ {
+ // skip 100
+ skip += 100;
+ var tmpResponse = FindNameGlobbingWithTag(packageName, tags, type, includePrerelease, skip, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+
+ return responses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for single name with version range.
+ /// Name: no wildcard support
+ /// Version: supports wildcards
+ /// Examples: Search "PowerShellGet" "[3.0.0.0, 5.0.0.0]"
+ /// Search "PowerShellGet" "3.*"
+ /// API Call: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet'
+ /// Implementation note: Returns all versions, including prerelease ones. Later (in the API client side) we'll do filtering on the versions to satisfy what user provided.
+ ///
+ public override string[] FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, out ExceptionDispatchInfo edi)
+ {
+ List responses = new List();
+ int skip = 0;
+
+ var initialResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(initialResponse);
+
+ if (!getOnlyLatest)
+ {
+ int initalCount = GetCountFromResponse(initialResponse, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ int count = initalCount / 100;
+
+ while (count > 0)
+ {
+ // skip 100
+ skip += 100;
+ var tmpResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+ responses.Add(tmpResponse);
+ count--;
+ }
+ }
+
+ return responses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for single name with specific version.
+ /// Name: no wildcard support
+ /// Version: no wildcard support
+ /// Examples: Search "PowerShellGet" "2.2.5"
+ /// API call: http://www.powershellgallery.com/api/v2/Packages(Id='PowerShellGet', Version='2.2.5')
+ ///
+ public override string FindVersion(string packageName, string version, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ // https://www.powershellgallery.com/api/v2//FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion eq '1.1.0' and substringof('PSModule', Tags) eq true
+ // Quotations around package name and version do not matter, same metadata gets returned.
+ string typeFilterPart = type == ResourceType.None ? String.Empty : $" and substringof('PS{type.ToString()}', Tags) eq true";
+ var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'&$filter= NormalizedVersion eq '{version}'{typeFilterPart}&{select}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ public override string FindVersionWithTag(string packageName, string version, string[] tags, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ string typeFilterPart = type == ResourceType.None ? String.Empty : $" and substringof('PS{type.ToString()}', Tags) eq true";
+
+ string tagFilterPart = String.Empty;
+ foreach (string tag in tags)
+ {
+ tagFilterPart += $" and substringof('{tag}', Tags) eq true";
+ }
+
+ var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'&$filter= NormalizedVersion eq '{version}'{typeFilterPart}{tagFilterPart}&{select}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+
+ /** INSTALL APIS **/
+
+ ///
+ /// Installs specific package.
+ /// Name: no wildcard support.
+ /// Examples: Install "PowerShellGet"
+ /// Implementation Note: if not prerelease: https://www.powershellgallery.com/api/v2/package/powershellget (Returns latest stable)
+ /// if prerelease, call into InstallVersion instead.
+ ///
+ public override HttpContent InstallName(string packageName, bool includePrerelease, out ExceptionDispatchInfo edi)
+ {
+ var requestUrlV2 = $"{repository.Uri}/package/{packageName}";
+
+ return HttpRequestCallForContent(requestUrlV2, out edi);
+ }
+
+ ///
+ /// Installs package with specific name and version.
+ /// Name: no wildcard support.
+ /// Version: no wildcard support.
+ /// Examples: Install "PowerShellGet" -Version "3.0.0.0"
+ /// Install "PowerShellGet" -Version "3.0.0-beta16"
+ /// API Call: https://www.powershellgallery.com/api/v2/package/Id/version (version can be prerelease)
+ ///
+ public override HttpContent InstallVersion(string packageName, string version, out ExceptionDispatchInfo edi)
+ {
+ var requestUrlV2 = $"{repository.Uri}/package/{packageName}/{version}";
+
+ return HttpRequestCallForContent(requestUrlV2, out edi);
+ }
+
+
+ private string HttpRequestCall(string requestUrlV2, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ string response = string.Empty;
+
+ try
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2);
+
+ response = SendV2RequestAsync(request, s_client).GetAwaiter().GetResult();
+ }
+ catch (HttpRequestException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (ArgumentNullException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (InvalidOperationException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+
+ return response;
+ }
+
+ private HttpContent HttpRequestCallForContent(string requestUrlV2, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ HttpContent content = null;
+
+ try
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2);
+
+ content = SendV2RequestForContentAsync(request, s_client).GetAwaiter().GetResult();
+ }
+ catch (HttpRequestException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (ArgumentNullException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (InvalidOperationException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+
+ return content;
+ }
+
+
+ #endregion
+
+ #region Private Methods
+
+ ///
+ /// Helper method for string[] FindAll(string, PSRepositoryInfo, bool, bool, ResourceType, out string)
+ ///
+ private string FindAllFromTypeEndPoint(bool includePrerelease, bool isSearchingModule, int skip, out ExceptionDispatchInfo edi)
+ {
+ string typeEndpoint = isSearchingModule ? String.Empty : "/items/psscript";
+ string paginationParam = $"&$orderby=Id desc&$inlinecount=allpages&$skip={skip}&$top=6000";
+ var prereleaseFilter = includePrerelease ? "IsAbsoluteLatestVersion&includePrerelease=true" : "IsLatestVersion";
+
+ var requestUrlV2 = $"{repository.Uri}{typeEndpoint}/Search()?$filter={prereleaseFilter}{paginationParam}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ ///
+ /// Helper method for string[] FindTag(string, PSRepositoryInfo, bool, bool, ResourceType, out string)
+ ///
+ private string FindTagFromEndpoint(string tag, bool includePrerelease, bool isSearchingModule, int skip, out ExceptionDispatchInfo edi)
+ {
+ // scenarios with type + tags:
+ // type: None -> search both endpoints
+ // type: M -> just search Module endpoint
+ // type: S -> just search Scripts end point
+ // type: DSCResource -> just search Modules
+ // type: Command -> just search Modules
+ string typeEndpoint = isSearchingModule ? String.Empty : "/items/psscript";
+ string paginationParam = $"&$orderby=Id desc&$inlinecount=allpages&$skip={skip}&$top=6000";
+ var prereleaseFilter = includePrerelease ? "$filter=IsAbsoluteLatestVersion&includePrerelease=true" : "$filter=IsLatestVersion";
+
+ var scriptsRequestUrlV2 = $"{repository.Uri}{typeEndpoint}/Search()?{prereleaseFilter}&searchTerm='tag:{tag}'{paginationParam}&{select}";
+
+ return HttpRequestCall(requestUrlV2: scriptsRequestUrlV2, out edi);
+ }
+
+ ///
+ /// Helper method for string[] FindCommandOrDSCResource(string, PSRepositoryInfo, bool, bool, ResourceType, out string)
+ ///
+ private string FindCommandOrDscResource(string tag, bool includePrerelease, bool isSearchingForCommands, int skip, out ExceptionDispatchInfo edi)
+ {
+ // can only find from Modules endpoint
+ string paginationParam = $"&$orderby=Id desc&$inlinecount=allpages&$skip={skip}&$top=6000";
+ var prereleaseFilter = includePrerelease ? "$filter=IsAbsoluteLatestVersion&includePrerelease=true" : "$filter=IsLatestVersion";
+ var tagFilter = isSearchingForCommands ? "PSCommand_" : "PSDscResource_";
+ var requestUrlV2 = $"{repository.Uri}/Search()?{prereleaseFilter}&searchTerm='tag:{tagFilter}{tag}'{prereleaseFilter}{paginationParam}&{select}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ ///
+ /// Helper method for string[] FindNameGlobbing()
+ ///
+ private string FindNameGlobbing(string packageName, ResourceType type, bool includePrerelease, int skip, out ExceptionDispatchInfo edi)
+ {
+ // https://www.powershellgallery.com/api/v2/Search()?$filter=endswith(Id, 'Get') and startswith(Id, 'PowerShell') and IsLatestVersion (stable)
+ // https://www.powershellgallery.com/api/v2/Search()?$filter=endswith(Id, 'Get') and IsAbsoluteLatestVersion&includePrerelease=true
+
+ string extraParam = $"&$orderby=Id desc&$inlinecount=allpages&$skip={skip}&$top=100";
+ var prerelease = includePrerelease ? "IsAbsoluteLatestVersion&includePrerelease=true" : "IsLatestVersion";
+ string nameFilter;
+
+ var names = packageName.Split(new char[] {'*'}, StringSplitOptions.RemoveEmptyEntries);
+
+ if (names.Length == 0)
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name '*' for V2 server protocol repositories is not supported"));
+ return string.Empty;
+ }
+ if (names.Length == 1)
+ {
+ if (packageName.StartsWith("*") && packageName.EndsWith("*"))
+ {
+ // *get*
+ nameFilter = $"substringof('{names[0]}', Id)";
+ }
+ else if (packageName.EndsWith("*"))
+ {
+ // PowerShell*
+ nameFilter = $"startswith(Id, '{names[0]}')";
+ }
+ else
+ {
+ // *ShellGet
+ nameFilter = $"endswith(Id, '{names[0]}')";
+ }
+ }
+ else if (names.Length == 2 && !packageName.StartsWith("*") && !packageName.EndsWith("*"))
+ {
+ // *pow*get*
+ // pow*get -> only support this
+ // pow*get*
+ // *pow*get
+ nameFilter = $"startswith(Id, '{names[0]}') and endswith(Id, '{names[1]}')";
+ }
+ else
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name with wildcards is only supported for scenarios similar to the following examples: PowerShell*, *ShellGet, *Shell*."));
+ return string.Empty;
+ }
+
+ string typeFilterPart = type == ResourceType.None ? String.Empty : $" and substringof('PS{type.ToString()}', Tags) eq true";
+
+ var requestUrlV2 = $"{repository.Uri}/Search()?$filter={nameFilter}{typeFilterPart} and {prerelease}&{select}{extraParam}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ private string FindNameGlobbingWithTag(string packageName, string[] tags, ResourceType type, bool includePrerelease, int skip, out ExceptionDispatchInfo edi)
+ {
+ // https://www.powershellgallery.com/api/v2/Search()?$filter=endswith(Id, 'Get') and startswith(Id, 'PowerShell') and IsLatestVersion (stable)
+ // https://www.powershellgallery.com/api/v2/Search()?$filter=endswith(Id, 'Get') and IsAbsoluteLatestVersion&includePrerelease=true
+
+ string extraParam = $"&$orderby=Id desc&$inlinecount=allpages&$skip={skip}&$top=100";
+ var prerelease = includePrerelease ? "IsAbsoluteLatestVersion&includePrerelease=true" : "IsLatestVersion";
+ string nameFilter;
+
+ var names = packageName.Split(new char[] {'*'}, StringSplitOptions.RemoveEmptyEntries);
+
+ if (names.Length == 0)
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name '*' for V2 server protocol repositories is not supported"));
+ return string.Empty;
+ }
+ if (names.Length == 1)
+ {
+ if (packageName.StartsWith("*") && packageName.EndsWith("*"))
+ {
+ // *get*
+ nameFilter = $"substringof('{names[0]}', Id)";
+ }
+ else if (packageName.EndsWith("*"))
+ {
+ // PowerShell*
+ nameFilter = $"startswith(Id, '{names[0]}')";
+ }
+ else
+ {
+ // *ShellGet
+ nameFilter = $"endswith(Id, '{names[0]}')";
+ }
+ }
+ else if (names.Length == 2 && !packageName.StartsWith("*") && !packageName.EndsWith("*"))
+ {
+ // *pow*get*
+ // pow*get -> only support this
+ // pow*get*
+ // *pow*get
+ nameFilter = $"startswith(Id, '{names[0]}') and endswith(Id, '{names[1]}')";
+ }
+ else
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name with wildcards is only supported for scenarios similar to the following examples: PowerShell*, *ShellGet, *Shell*."));
+ return string.Empty;
+ }
+
+ string tagFilterPart = String.Empty;
+ foreach (string tag in tags)
+ {
+ tagFilterPart += $" and substringof('{tag}', Tags) eq true";
+ }
+
+ string typeFilterPart = type == ResourceType.None ? String.Empty : $" and substringof('PS{type.ToString()}', Tags) eq true";
+ var requestUrlV2 = $"{repository.Uri}/Search()?$filter={nameFilter}{tagFilterPart}{typeFilterPart} and {prerelease}&{select}{extraParam}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ ///
+ /// Helper method for string[] FindVersionGlobbing()
+ ///
+ private string FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, int skip, bool getOnlyLatest, out ExceptionDispatchInfo edi)
+ {
+ //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion gt '1.0.0' and NormalizedVersion lt '2.2.5' and substringof('PSModule', Tags) eq true
+ //https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= NormalizedVersion gt '1.1.1' and NormalizedVersion lt '2.2.5'
+ // NormalizedVersion doesn't include trailing zeroes
+ // Notes: this could allow us to take a version range (i.e (2.0.0, 3.0.0.0]) and deconstruct it and add options to the Filter for Version to describe that range
+ // will need to filter additionally, if IncludePrerelease=false, by default we get stable + prerelease both back
+ // Current bug: Find PSGet -Version "2.0.*" -> https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= Version gt '2.0.*' and Version lt '2.1'
+ // Make sure to include quotations around the package name
+
+ //and IsPrerelease eq false
+ // ex:
+ // (2.0.0, 3.0.0)
+ // $filter= NVersion gt '2.0.0' and NVersion lt '3.0.0'
+
+ // [2.0.0, 3.0.0]
+ // $filter= NVersion ge '2.0.0' and NVersion le '3.0.0'
+
+ // [2.0.0, 3.0.0)
+ // $filter= NVersion ge '2.0.0' and NVersion lt '3.0.0'
+
+ // (2.0.0, 3.0.0]
+ // $filter= NVersion gt '2.0.0' and NVersion le '3.0.0'
+
+ // [, 2.0.0]
+ // $filter= NVersion le '2.0.0'
+
+ string format = "NormalizedVersion {0} {1}";
+ string minPart = String.Empty;
+ string maxPart = String.Empty;
+
+ if (versionRange.MinVersion != null)
+ {
+ string operation = versionRange.IsMinInclusive ? "ge" : "gt";
+ minPart = String.Format(format, operation, $"'{versionRange.MinVersion.ToNormalizedString()}'");
+ }
+
+ if (versionRange.MaxVersion != null)
+ {
+ string operation = versionRange.IsMaxInclusive ? "le" : "lt";
+ maxPart = String.Format(format, operation, $"'{versionRange.MaxVersion.ToNormalizedString()}'");
+ }
+
+ string versionFilterParts = String.Empty;
+ if (!String.IsNullOrEmpty(minPart) && !String.IsNullOrEmpty(maxPart))
+ {
+ versionFilterParts += minPart + " and " + maxPart;
+ }
+ else if (!String.IsNullOrEmpty(minPart))
+ {
+ versionFilterParts += minPart;
+ }
+ else if (!String.IsNullOrEmpty(maxPart))
+ {
+ versionFilterParts += maxPart;
+ }
+
+ string filterQuery = "&$filter=";
+ filterQuery += includePrerelease ? string.Empty : "IsPrerelease eq false";
+ //filterQuery += type == ResourceType.None ? String.Empty : $" and substringof('PS{type.ToString()}', Tags) eq true";
+
+ string joiningOperator = filterQuery.EndsWith("=") ? String.Empty : " and " ;
+ filterQuery += type == ResourceType.None ? String.Empty : $"{joiningOperator}substringof('PS{type.ToString()}', Tags) eq true";
+
+ if (!String.IsNullOrEmpty(versionFilterParts))
+ {
+ // Check if includePrerelease is true, if it is we want to add "$filter"
+ // Single case where version is "*" (or "[,]") and includePrerelease is true, then we do not want to add "$filter" to the requestUrl.
+
+ // Note: could be null/empty if Version was "*" -> [,]
+ joiningOperator = filterQuery.EndsWith("=") ? String.Empty : " and " ;
+ filterQuery += $"{joiningOperator}{versionFilterParts}";
+ }
+
+ string topParam = getOnlyLatest ? "$top=1" : "$top=100"; // only need 1 package if interested in latest
+ string paginationParam = $"$inlinecount=allpages&$skip={skip}&{topParam}";
+
+ filterQuery = filterQuery.EndsWith("=") ? string.Empty : filterQuery;
+ var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'&$orderby=NormalizedVersion desc&{paginationParam}&{select}{filterQuery}";
+
+ return HttpRequestCall(requestUrlV2, out edi);
+ }
+
+ public int GetCountFromResponse(string httpResponse, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ int count = 0;
+
+ //Create the XmlDocument.
+ XmlDocument doc = new XmlDocument();
+
+ try
+ {
+ doc.LoadXml(httpResponse);
+ }
+ catch (XmlException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ if (edi != null)
+ {
+ return count;
+ }
+
+ XmlNodeList elemList = doc.GetElementsByTagName("m:count");
+ if (elemList.Count > 0)
+ {
+ XmlNode node = elemList[0];
+ count = int.Parse(node.InnerText);
+ }
+
+ return count;
+ }
+
+ public static async Task SendV2RequestAsync(HttpRequestMessage message, HttpClient s_client)
+ {
+ string errMsg = "Error occured while trying to retrieve response: ";
+ try
+ {
+ HttpResponseMessage response = await s_client.SendAsync(message);
+ response.EnsureSuccessStatusCode();
+ return response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
+ }
+ catch (HttpRequestException e)
+ {
+ throw new HttpRequestException(errMsg + e.Message);
+ }
+ catch (ArgumentNullException e)
+ {
+ throw new ArgumentNullException(errMsg + e.Message);
+ }
+ catch (InvalidOperationException e)
+ {
+ throw new InvalidOperationException(errMsg + e.Message);
+ }
+ }
+
+ public static async Task SendV2RequestForContentAsync(HttpRequestMessage message, HttpClient s_client)
+ {
+ string errMsg = "Error occured while trying to retrieve response for content: ";
+ try
+ {
+ HttpResponseMessage response = await s_client.SendAsync(message);
+ response.EnsureSuccessStatusCode();
+ return response.Content;
+ }
+ catch (HttpRequestException e)
+ {
+ throw new HttpRequestException(errMsg + e.Message);
+ }
+ catch (ArgumentNullException e)
+ {
+ throw new ArgumentNullException(errMsg + e.Message);
+ }
+ catch (InvalidOperationException e)
+ {
+ throw new InvalidOperationException(errMsg + e.Message);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/V3ResponseUtil.cs b/src/code/V3ResponseUtil.cs
new file mode 100644
index 000000000..e28490e46
--- /dev/null
+++ b/src/code/V3ResponseUtil.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text.Json;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal class V3ResponseUtil : ResponseUtil
+ {
+ #region Members
+
+ public override PSRepositoryInfo repository { get; set; }
+
+ #endregion
+
+ #region Constructor
+
+ public V3ResponseUtil(PSRepositoryInfo repository) : base(repository)
+ {
+ this.repository = repository;
+ }
+
+ #endregion
+
+ #region Overriden Methods
+
+ public override IEnumerable ConvertToPSResourceResult(string[] responses)
+ {
+ // in FindHelper:
+ // serverApi.FindName() -> return responses, and out errRecord
+ // check outErrorRecord
+ //
+ // v3Converter.ConvertToPSResourceInfo(responses) -> return PSResourceResult
+ // check resourceResult for error, write if needed
+
+ foreach (string response in responses)
+ {
+ string parseError = String.Empty;
+ JsonDocument pkgVersionEntry = null;
+ try
+ {
+ pkgVersionEntry = JsonDocument.Parse(response);
+ }
+ catch (Exception e)
+ {
+ parseError = e.Message;
+ }
+
+ if (!String.IsNullOrEmpty(parseError))
+ {
+ yield return new PSResourceResult(returnedObject: null, errorMsg: parseError, isTerminatingError: false);
+ }
+
+ if (!PSResourceInfo.TryConvertFromJson(pkgVersionEntry, out PSResourceInfo psGetInfo, repository.Name, out string errorMsg))
+ {
+ yield return new PSResourceResult(returnedObject: null, errorMsg: errorMsg, isTerminatingError: false);
+ }
+
+ yield return new PSResourceResult(returnedObject: psGetInfo, errorMsg: String.Empty, isTerminatingError: false);
+ }
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/src/code/V3ServerAPICalls.cs b/src/code/V3ServerAPICalls.cs
new file mode 100644
index 000000000..64a83da77
--- /dev/null
+++ b/src/code/V3ServerAPICalls.cs
@@ -0,0 +1,1072 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+using NuGet.Versioning;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Linq;
+using System.Net;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Collections;
+using System.Runtime.ExceptionServices;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ internal class V3ServerAPICalls : ServerApiCall
+ {
+ #region Members
+ public override PSRepositoryInfo repository { get; set; }
+ public override HttpClient s_client { get; set; }
+
+ private static readonly string resourcesName = "resources";
+ private static readonly string packageBaseAddressName = "PackageBaseAddress/3.0.0";
+ private static readonly string searchQueryServiceName = "SearchQueryService/3.0.0-beta";
+ private static readonly string registrationsBaseUrlName = "RegistrationsBaseUrl/Versioned";
+ private static readonly string dataName = "data";
+ private static readonly string idName = "id";
+ private static readonly string versionName = "version";
+ private static readonly string tagsName = "tags";
+ private static readonly string versionsName = "versions";
+
+ #endregion
+
+ #region Constructor
+
+ public V3ServerAPICalls(PSRepositoryInfo repository, NetworkCredential networkCredential) : base(repository, networkCredential)
+ {
+ this.repository = repository;
+
+ HttpClientHandler handler = new HttpClientHandler()
+ {
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ Credentials = networkCredential
+ };
+
+ s_client = new HttpClient(handler);
+
+ }
+
+ #endregion
+
+ #region Overriden Methods
+ // High level design: Find-PSResource >>> IFindPSResource (loops, version checks, etc.) >>> IServerAPICalls (call to repository endpoint/url)
+
+ ///
+ /// Find method which allows for searching for all packages from a repository and returns latest version for each.
+ /// Not supported
+ ///
+ public override string[] FindAll(bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ string errMsg = $"Find all is not supported for the repository {repository.Uri}";
+ edi = ExceptionDispatchInfo.Capture(new OperationNotSupportedException(errMsg));
+
+ return Utils.EmptyStrArray;
+ }
+
+ ///
+ /// Find method which allows for searching for packages with tag from a repository and returns latest version for each.
+ /// Examples: Search -Tag "Redis" -Repository PSGallery
+ /// API call:
+ /// https://azuresearch-ussc.nuget.org/query?q=tags:redis&prerelease=False&semVerLevel=2.0.0
+ ///
+ /// Azure Artifacts does not support querying on tags, so if support this scenario we need to search on the term and then filter
+ ///
+ public override string[] FindTag(string tag, bool includePrerelease, ResourceType _type, out ExceptionDispatchInfo edi)
+ {
+ List responses = new List();
+
+ Hashtable resourceUrls = FindResourceType(new string[] { searchQueryServiceName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+
+ string searchQueryServiceUrl = resourceUrls[searchQueryServiceName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ bool isNuGetRepo = searchQueryServiceUrl.Contains("nuget.org");
+
+ string query = isNuGetRepo ? $"{searchQueryServiceUrl}?q=tags:{tag.ToLower()}&prerelease={includePrerelease}&semVerLevel=2.0.0" :
+ $"{searchQueryServiceUrl}?q={tag.ToLower()}&prerelease={includePrerelease}&semVerLevel=2.0.0";
+
+ // 2) call query with tags. (for Azure artifacts) get unique names, see which ones truly match
+ JsonElement[] tagPkgs = GetJsonElementArr(query, dataName, out edi);
+ if (edi != null)
+ {
+ return responses.ToArray();
+ }
+
+ List matchingResponses = new List();
+ string id;
+ string latestVersion;
+ foreach (var pkgId in tagPkgs)
+ {
+ try
+ {
+ if (!pkgId.TryGetProperty(idName, out JsonElement idItem) || !pkgId.TryGetProperty(versionName, out JsonElement versionItem))
+ {
+ string errMsg = $"FindTag(): Id or Version element could not be found in response.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ id = idItem.ToString();
+ latestVersion = versionItem.ToString();
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"FindTag(): Id or Version element could not be parsed from response due to exception {e.Message}.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ // determine if id matches our wildcard criteria
+ if (isNuGetRepo)
+ {
+ string response = FindVersionHelper(registrationsBaseUrl, id, latestVersion, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ matchingResponses.Add(response);
+ }
+ else
+ {
+ try {
+ if (!pkgId.TryGetProperty("tags", out JsonElement tagsItem))
+ {
+ string errMsg = $"FindTag(): Tag element could not be found in response.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ foreach (var tagItem in tagsItem.EnumerateArray())
+ {
+ if (tag.Equals(tagItem.ToString(), StringComparison.InvariantCultureIgnoreCase))
+ {
+ string response = FindVersionHelper(registrationsBaseUrl, id, latestVersion, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ matchingResponses.Add(response);
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"FindTag(): Tags element could not be parsed from response due to exception {e.Message}.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+ }
+ }
+
+ return matchingResponses.ToArray();
+ }
+
+ public override string[] FindCommandOrDscResource(string tag, bool includePrerelease, bool isSearchingForCommands, out ExceptionDispatchInfo edi)
+ {
+ string errMsg = $"Find by CommandName or DSCResource is not supported for {repository.Name} as it uses the V3 server protocol";
+ edi = ExceptionDispatchInfo.Capture(new OperationNotSupportedException(errMsg));
+
+ return Utils.EmptyStrArray;
+ }
+
+ ///
+ /// Find method which allows for searching for single name and returns latest version.
+ /// Name: no wildcard support
+ /// Examples: Search "Newtonsoft.Json"
+ /// API call:
+ /// https://api.nuget.org/v3/registration5-gz-semver2/nuget.server/index.json
+ /// https://msazure.pkgs.visualstudio.com/One/_packaging/testfeed/nuget/v3/registrations2-semver2/newtonsoft.json/index.json
+ /// https://msazure.pkgs.visualstudio.com/999aa88e-7ed7-41b2-9d77-5bc261222004/_packaging/0d5429e2-c871-4347-bdc9-d1cbbac5eb3b/nuget/v3/registrations2-semver2/newtonsoft.json/index.json
+ /// The RegistrationBaseUrl that we're using is "RegistrationBaseUrl/Versioned"
+ /// This type points to the url to use (ex above)
+ /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference)
+ ///
+ public override string FindName(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { packageBaseAddressName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string packageBaseAddressUrl = resourceUrls[packageBaseAddressName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ bool isNuGetRepo = packageBaseAddressUrl.Contains("v3-flatcontainer");
+ JsonElement[] pkgVersionsArr = GetPackageVersions(packageBaseAddressUrl, packageName, isNuGetRepo, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string response = string.Empty;
+ foreach (JsonElement version in pkgVersionsArr)
+ {
+ // parse as NuGetVersion
+ if (NuGetVersion.TryParse(version.ToString(), out NuGetVersion nugetVersion))
+ {
+ /*
+ * pkgVersion == !prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == !prerelease && includePrerelease == false --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == false --> throw away pkg
+ */
+ if (!nugetVersion.IsPrerelease || includePrerelease)
+ {
+ response = FindVersionHelper(registrationsBaseUrl, packageName, version.ToString(), out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (String.IsNullOrEmpty(response))
+ {
+ edi = ExceptionDispatchInfo.Capture(new InvalidOrEmptyResponse($"FindName() with {packageName} returned empty response."));
+ }
+
+ return response;
+ }
+
+ public override string FindNameWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { packageBaseAddressName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string packageBaseAddressUrl = resourceUrls[packageBaseAddressName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ bool isNuGetRepo = packageBaseAddressUrl.Contains("v3-flatcontainer");
+ JsonElement[] pkgVersionsArr = GetPackageVersions(packageBaseAddressUrl, packageName, isNuGetRepo, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string response = string.Empty;
+ foreach (JsonElement version in pkgVersionsArr)
+ {
+ // parse as NuGetVersion
+ if (NuGetVersion.TryParse(version.ToString(), out NuGetVersion nugetVersion))
+ {
+ /*
+ * pkgVersion == !prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == !prerelease && includePrerelease == false --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == false --> throw away pkg
+ */
+ if (!nugetVersion.IsPrerelease || includePrerelease)
+ {
+ response = FindVersionHelper(registrationsBaseUrl, packageName, version.ToString(), out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ bool isTagMatch = DetermineTagsPresent(response: response, tags: tags, out edi);
+
+ if (!isTagMatch)
+ {
+ if (edi == null)
+ {
+ string errMsg = $"FindNameWithTag(): Tags required were not found in package {packageName} {version.ToString()}.";
+ edi = ExceptionDispatchInfo.Capture(new SpecifiedTagsNotFoundException(errMsg));
+ }
+
+ return String.Empty;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (String.IsNullOrEmpty(response))
+ {
+ edi = ExceptionDispatchInfo.Capture(new InvalidOrEmptyResponse($"FindNameWithTag() with {packageName} and tags {String.Join(",", tags)} returned empty response."));
+ }
+
+ return response;
+ }
+
+ ///
+ /// Find method which allows for searching for single name with wildcards and returns latest version.
+ /// Name: supports wildcards
+ /// Examples: Search "Nuget.Server*"
+ /// API call:
+ /// - No prerelease: https://api-v2v3search-0.nuget.org/autocomplete?q=storage&prerelease=false
+ /// - Prerelease: https://api-v2v3search-0.nuget.org/autocomplete?q=storage&prerelease=true
+ ///
+ /// https://msazure.pkgs.visualstudio.com/b32aa71e-8ed2-41b2-9d77-5bc261222004/_packaging/0d5429e2-c871-4347-bdc9-d1cbbac5eb3b/nuget/v3/query2?q=Newtonsoft&prerelease=false&semVerLevel=2.0.0
+ ///
+ /// Note: response only returns names
+ ///
+ /// Make another query to get the latest version of each package (ie call "FindVersionGlobbing")
+ ///
+ public override string[] FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ var names = packageName.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
+ string querySearchTerm;
+
+ if (names.Length == 0)
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name '*' for V3 server protocol repositories is not supported"));
+ return Utils.EmptyStrArray;
+ }
+ if (names.Length == 1)
+ {
+ // packageName: *get* -> q: get
+ // packageName: PowerShell* -> q: PowerShell
+ // packageName: *ShellGet -> q: ShellGet
+ querySearchTerm = names[0];
+ }
+ else
+ {
+ // *pow*get*
+ // pow*get -> only support this (V2)
+ // pow*get*
+ // *pow*get
+
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name with wildcards is only supported for scenarios similar to the following examples: PowerShell*, *ShellGet, *Shell*."));
+ return Utils.EmptyStrArray;
+ }
+
+ // https://msazure.pkgs.visualstudio.com/.../_packaging/.../nuget/v3/query2 (no support for * in search term, but matches like NuGet)
+ // https://azuresearch-usnc.nuget.org/query?q=Newtonsoft&prerelease=false&semVerLevel=1.0.0 (NuGet) (supports * at end of searchterm q but equivalent to q = text w/o *)
+ Hashtable resourceUrls = FindResourceType(new string[] { searchQueryServiceName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ string searchQueryServiceUrl = resourceUrls[searchQueryServiceName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ string query = $"{searchQueryServiceUrl}?q={querySearchTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0";
+
+ // 2) call query with search term, get unique names, see which ones truly match
+ JsonElement[] matchingPkgIds = GetJsonElementArr(query, dataName, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ List matchingResponses = new List();
+ foreach (var pkgId in matchingPkgIds)
+ {
+ string id = string.Empty;
+ string latestVersion = string.Empty;
+
+ try
+ {
+ if (!pkgId.TryGetProperty(idName, out JsonElement idItem) || ! pkgId.TryGetProperty(versionName, out JsonElement versionItem))
+ {
+ string errMsg = $"FindNameGlobbing(): Name or Version element could not be found in response.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ id = idItem.ToString();
+ latestVersion = versionItem.ToString();
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"FindNameGlobbing(): Name or Version element could not be parsed from response due to exception {e.Message}.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ break;
+ }
+
+ // determine if id matches our wildcard criteria
+ if ((packageName.StartsWith("*") && packageName.EndsWith("*") && id.ToLower().Contains(querySearchTerm.ToLower())) ||
+ (packageName.EndsWith("*") && id.StartsWith(querySearchTerm, StringComparison.OrdinalIgnoreCase)) ||
+ (packageName.StartsWith("*") && id.EndsWith(querySearchTerm, StringComparison.OrdinalIgnoreCase)))
+ {
+ string response = FindVersionHelper(registrationsBaseUrl, id, latestVersion, out edi);
+
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ matchingResponses.Add(response);
+ }
+ }
+
+ return matchingResponses.ToArray();
+ }
+
+ public override string[] FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ var names = packageName.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
+ string querySearchTerm;
+
+ if (names.Length == 0)
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name '*' for V3 server protocol repositories is not supported"));
+ return Utils.EmptyStrArray;
+ }
+ if (names.Length == 1)
+ {
+ // packageName: *get* -> q: get
+ // packageName: PowerShell* -> q: PowerShell
+ // packageName: *ShellGet -> q: ShellGet
+ querySearchTerm = names[0];
+ }
+ else
+ {
+ // *pow*get*
+ // pow*get -> only support this (V2)
+ // pow*get*
+ // *pow*get
+
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException("-Name with wildcards is only supported for scenarios similar to the following examples: PowerShell*, *ShellGet, *Shell*."));
+ return Utils.EmptyStrArray;
+ }
+
+ // https://msazure.pkgs.visualstudio.com/.../_packaging/.../nuget/v3/query2 (no support for * in search term, but matches like NuGet)
+ // https://azuresearch-usnc.nuget.org/query?q=Newtonsoft&prerelease=false&semVerLevel=1.0.0 (NuGet) (supports * at end of searchterm q but equivalent to q = text w/o *)
+ Hashtable resourceUrls = FindResourceType(new string[] { searchQueryServiceName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ string searchQueryServiceUrl = resourceUrls[searchQueryServiceName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ string query = $"{searchQueryServiceUrl}?q={querySearchTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0";
+
+ // 2) call query with search term, get unique names, see which ones truly match
+ JsonElement[] matchingPkgIds = GetJsonElementArr(query, dataName, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ List matchingResponses = new List();
+ foreach (var pkgId in matchingPkgIds)
+ {
+ string id = string.Empty;
+ string latestVersion = string.Empty;
+ string[] pkgTags = Utils.EmptyStrArray;
+
+ try
+ {
+ if (!pkgId.TryGetProperty(idName, out JsonElement idItem) || !pkgId.TryGetProperty(versionName, out JsonElement versionItem))
+ {
+ string errMsg = $"FindNameGlobbing(): Name or Version element could not be found in response.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ if (!pkgId.TryGetProperty(tagsName, out JsonElement tagsItem))
+ {
+ string errMsg = $"FindNameGlobbing(): Tags element could not be found in response.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return Utils.EmptyStrArray;
+ }
+
+ id = idItem.ToString();
+ latestVersion = versionItem.ToString();
+
+ pkgTags = GetTagsFromJsonElement(tagsElement: tagsItem);
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"FindNameGlobbingWithTag(): Name or Version or Tags element could not be parsed from response due to exception {e.Message}.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ break;
+ }
+
+ // determine if id matches our wildcard criteria
+ if ((packageName.StartsWith("*") && packageName.EndsWith("*") && id.ToLower().Contains(querySearchTerm.ToLower())) ||
+ (packageName.EndsWith("*") && id.StartsWith(querySearchTerm, StringComparison.OrdinalIgnoreCase)) ||
+ (packageName.StartsWith("*") && id.EndsWith(querySearchTerm, StringComparison.OrdinalIgnoreCase)))
+ {
+ bool isTagMatch = DeterminePkgTagsSatisfyRequiredTags(pkgTags: pkgTags, requiredTags: tags);
+ if (!isTagMatch)
+ {
+ continue;
+ }
+
+ string response = FindVersionHelper(registrationsBaseUrl, id, latestVersion, out edi);
+
+ if (edi != null)
+ {
+ continue;
+ }
+
+ matchingResponses.Add(response);
+ }
+ }
+
+ return matchingResponses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for single name with version range.
+ /// Name: no wildcard support
+ /// Version: supports wildcards
+ /// Examples: Search "NuGet.Server.Core" "[1.0.0.0, 5.0.0.0]"
+ /// Search "NuGet.Server.Core" "3.*"
+ /// API Call:
+ /// then, find all versions for a pkg
+ /// for nuget:
+ /// this contains all pkg version info: https://api.nuget.org/v3/registration5-gz-semver2/nuget.server/index.json
+ /// However, we will use the flattened version list: https://api.nuget.org/v3-flatcontainer/newtonsoft.json/index.json
+ /// for Azure Artifacts:
+ /// https://msazure.pkgs.visualstudio.com/b32aa71e-8ed2-41b2-9d77-5bc261222004/_packaging/0d5429e2-c871-4347-bdc9-d1cbbac5eb3b/nuget/v3/flat2/newtonsoft.json/index.json
+ /// (azure artifacts)
+ ///
+ /// Note: very different responses for nuget vs azure artifacts
+ ///
+ /// After we figure out what version we want, call "FindVersion" (or some helper method)
+ /// need to filter client side
+ /// Implementation note: Returns all versions, including prerelease ones. Later (in the API client side) we'll do filtering on the versions to satisfy what user provided.
+ ///
+ public override string[] FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { packageBaseAddressName, registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ string packageBaseAddressUrl = resourceUrls[packageBaseAddressName] as string;
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ bool isNuGetRepo = packageBaseAddressUrl.Contains("v3-flatcontainer");
+ JsonElement[] pkgVersionsArr = GetPackageVersions(packageBaseAddressUrl, packageName, isNuGetRepo, out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ List responses = new List();
+ foreach (var version in pkgVersionsArr) {
+ if (NuGetVersion.TryParse(version.ToString(), out NuGetVersion nugetVersion) && versionRange.Satisfies(nugetVersion))
+ {
+ /*
+ * pkgVersion == !prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == !prerelease && includePrerelease == false --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == false --> throw away pkg
+ */
+ if (!nugetVersion.IsPrerelease || includePrerelease) {
+ string response = FindVersionHelper(registrationsBaseUrl, packageName, version.ToString(), out edi);
+ if (edi != null)
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ responses.Add(response);
+ }
+ }
+ }
+
+ return responses.ToArray();
+ }
+
+ ///
+ /// Find method which allows for searching for single name with specific version.
+ /// Name: no wildcard support
+ /// Version: no wildcard support
+ /// Examples: Search "NuGet.Server.Core" "3.0.0-beta"
+ /// API call:
+ /// first find the RegistrationBaseUrl
+ /// https://api.nuget.org/v3/registration5-gz-semver2/nuget.server/index.json
+ ///
+ /// https://msazure.pkgs.visualstudio.com/One/_packaging/testfeed/nuget/v3/registrations2-semver2/newtonsoft.json/index.json
+ /// https://msazure.pkgs.visualstudio.com/999aa88e-7ed7-41b2-9d77-5bc261222004/_packaging/0d5429e2-c871-4347-bdc9-d1cbbac5eb3b/nuget/v3/registrations2-semver2/newtonsoft.json/index.json
+ /// The RegistrationBaseUrl that we're using is "RegistrationBaseUrl/Versioned"
+ /// This type points to the url to use (ex above)
+ ///
+ /// then we can make a call for the specific version
+ /// https://api.nuget.org/v3/registration5-gz-semver2/nuget.server.core/3.0.0-beta
+ /// (alternative url for nuget gallery): https://api.nuget.org/v3/registration5-gz-semver2/nuget.server.core/index.json#page/3.0.0-beta/3.0.0-beta
+ /// https://msazure.pkgs.visualstudio.com/b32aa71e-8ed2-41b2-9d77-5bc261222004/_packaging/0d5429e2-c871-4347-bdc9-d1cbbac5eb3b/nuget/v3/registrations2/newtonsoft.json/13.0.2.json
+ ///
+ ///
+ public override string FindVersion(string packageName, string version, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ string response = FindVersionHelper(registrationsBaseUrl, packageName, version, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ if (String.IsNullOrEmpty(response))
+ {
+ edi = ExceptionDispatchInfo.Capture(new InvalidOrEmptyResponse($"FindVersion() with {packageName} and version {version} returned empty response."));
+ }
+
+ return response;
+ }
+
+ public override string FindVersionWithTag(string packageName, string version, string[] tags, ResourceType type, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { registrationsBaseUrlName }, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string registrationsBaseUrl = resourceUrls[registrationsBaseUrlName] as string;
+
+ string response = FindVersionHelper(registrationsBaseUrl, packageName, version, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ bool isTagMatch = DetermineTagsPresent(response: response, tags: tags, out edi);
+
+ if (!isTagMatch)
+ {
+ if (edi == null)
+ {
+ string errMsg = $"FindVersionWithTag(): Tags required were not found in package {packageName} {version.ToString()}.";
+ edi = ExceptionDispatchInfo.Capture(new SpecifiedTagsNotFoundException(errMsg));
+ }
+
+ return String.Empty;
+ }
+
+ if (String.IsNullOrEmpty(response))
+ {
+ edi = ExceptionDispatchInfo.Capture(new InvalidOrEmptyResponse($"FindVersion() with {packageName}, tags {String.Join(", ", tags)} and version {version} returned empty response."));
+ }
+
+ return response;
+ }
+
+
+ /** INSTALL APIS **/
+
+ ///
+ /// Installs specific package.
+ /// Name: no wildcard support.
+ /// Examples: Install "PowerShellGet"
+ /// Implementation Note: if not prerelease: https://www.powershellgallery.com/api/v2/package/powershellget (Returns latest stable)
+ /// if prerelease, the calling method should first call IFindPSResource.FindName(),
+ /// then find the exact version to install, then call into install version
+ ///
+ public override HttpContent InstallName(string packageName, bool includePrerelease, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { packageBaseAddressName }, out edi);
+ if (edi != null)
+ {
+ return null;
+ }
+
+ string packageBaseAddressUrl = resourceUrls[packageBaseAddressName] as string;
+
+ bool isNuGetRepo = packageBaseAddressUrl.Contains("v3-flatcontainer");
+
+ JsonElement[] pkgVersionsArr = GetPackageVersions(packageBaseAddressUrl, packageName, isNuGetRepo, out edi);
+ if (edi != null)
+ {
+ return null;
+ }
+
+ foreach (JsonElement version in pkgVersionsArr)
+ {
+ if (NuGetVersion.TryParse(version.ToString(), out NuGetVersion nugetVersion))
+ {
+ /*
+ * pkgVersion == !prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == !prerelease && includePrerelease == false --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == true --> keep pkg
+ * pkgVersion == prerelease && includePrerelease == false --> throw away pkg
+ */
+ if (!nugetVersion.IsPrerelease || includePrerelease)
+ {
+ var response = InstallVersion(packageName, version.ToString(), out edi);
+ if (edi != null)
+ {
+ return null;
+ }
+
+ return response;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Installs package with specific name and version.
+ /// Name: no wildcard support.
+ /// Version: no wildcard support.
+ /// Examples: Install "PowerShellGet" -Version "3.0.0.0"
+ /// Install "PowerShellGet" -Version "3.0.0-beta16"
+ ///
+ /// https://api.nuget.org/v3-flatcontainer/newtonsoft.json/9.0.1/newtonsoft.json.9.0.1.nupkg
+ /// API Call:
+ ///
+ public override HttpContent InstallVersion(string packageName, string version, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceUrls = FindResourceType(new string[] { packageBaseAddressName }, out edi);
+ if (edi != null)
+ {
+ return null;
+ }
+
+ string packageBaseAddressUrl = resourceUrls[packageBaseAddressName] as string;
+
+ string pkgName = packageName.ToLower();
+ string installPkgUrl = $"{packageBaseAddressUrl}{pkgName}/{version}/{pkgName}.{version}.nupkg";
+
+ var content = HttpRequestCallForContent(installPkgUrl, out edi);
+ if (edi != null)
+ {
+ return null;
+ }
+
+ return content;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private String HttpRequestCall(string requestUrlV3, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ string response = string.Empty;
+
+ try
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV3);
+
+ response = SendV3RequestAsync(request, s_client).GetAwaiter().GetResult();
+ }
+ catch (HttpRequestException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (ArgumentNullException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (InvalidOperationException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (Exception e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+
+ return response;
+ }
+
+ private HttpContent HttpRequestCallForContent(string requestUrlV3, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ HttpContent content = null;
+
+ try
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV3);
+
+ content = SendV3RequestForContentAsync(request, s_client).GetAwaiter().GetResult();
+ }
+ catch (HttpRequestException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (ArgumentNullException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+ catch (InvalidOperationException e)
+ {
+ edi = ExceptionDispatchInfo.Capture(e);
+ }
+
+ return content;
+ }
+
+ private Hashtable FindResourceType(string[] resourceTypeName, out ExceptionDispatchInfo edi)
+ {
+ Hashtable resourceHash = new Hashtable();
+ JsonElement[] resources = GetJsonElementArr($"{repository.Uri}", resourcesName, out edi);
+ if (edi != null)
+ {
+ return resourceHash;
+ }
+
+ foreach (JsonElement resource in resources)
+ {
+ try
+ {
+ if (resource.TryGetProperty("@type", out JsonElement typeElement) && resourceTypeName.Contains(typeElement.ToString()))
+ {
+ // check if key already present in hastable, as there can be resources with same type but primary/secondary instances
+ if (!resourceHash.ContainsKey(typeElement.ToString()))
+ {
+ if (resource.TryGetProperty("@id", out JsonElement idElement))
+ {
+ // add name of the resource and its url
+ resourceHash.Add(typeElement.ToString(), idElement.ToString());
+ }
+ else
+ {
+ string errMsg = $"@type element was found but @id element not found in service index '{repository.Uri}' for {resourceTypeName}.";
+ edi = ExceptionDispatchInfo.Capture(new V3ResourceNotFoundException(errMsg));
+ return resourceHash;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"Exception parsing JSON for respository {repository.Uri} with error: {e.Message}";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return resourceHash;
+ }
+
+ if (resourceHash.Count == resourceTypeName.Length)
+ {
+ break;
+ }
+ }
+
+ foreach (string resourceType in resourceTypeName)
+ {
+ if (!resourceHash.ContainsKey(resourceType))
+ {
+ string errMsg = $"FindResourceType(): Could not find resource type {resourceType} from the service index.";
+ edi = ExceptionDispatchInfo.Capture(new V3ResourceNotFoundException(errMsg));
+ break;
+ }
+ }
+
+ return resourceHash;
+ }
+
+ private string FindVersionHelper(string registrationsBaseUrl, string packageName, string version, out ExceptionDispatchInfo edi)
+ {
+ // https://api.nuget.org/v3/registration5-gz-semver2/newtonsoft.json/13.0.2.json
+ var requestPkgMapping = $"{registrationsBaseUrl}{packageName.ToLower()}/{version}.json";
+ string pkgMappingResponse = HttpRequestCall(requestPkgMapping, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ string catalogEntryUrl = string.Empty;
+ try
+ {
+ JsonDocument pkgMappingDom = JsonDocument.Parse(pkgMappingResponse);
+ JsonElement rootPkgMappingDom = pkgMappingDom.RootElement;
+
+ if (!rootPkgMappingDom.TryGetProperty("catalogEntry", out JsonElement catalogEntryUrlElement))
+ {
+ string errMsg = $"FindVersionHelper(): CatalogEntry element could not be found in response or was empty.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return String.Empty;
+ }
+
+ catalogEntryUrl = catalogEntryUrlElement.ToString();
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"FindVersionHelper(): Exception parsing JSON for respository {repository.Uri} with error: {e.Message}";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return String.Empty;
+ }
+
+ string response = HttpRequestCall(catalogEntryUrl, out edi);
+ if (edi != null)
+ {
+ return String.Empty;
+ }
+
+ return response;
+ }
+
+ private bool DetermineTagsPresent(string response, string[] tags, out ExceptionDispatchInfo edi)
+ {
+ edi = null;
+ string[] pkgTags = Utils.EmptyStrArray;
+
+ try
+ {
+ JsonDocument pkgMappingDom = JsonDocument.Parse(response);
+ JsonElement rootPkgMappingDom = pkgMappingDom.RootElement;
+
+ if (!rootPkgMappingDom.TryGetProperty(tagsName, out JsonElement tagsElement))
+ {
+ string errMsg = $"FindNameWithTag(): Tags element could not be found in response or was empty.";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return false;
+ }
+
+ pkgTags = GetTagsFromJsonElement(tagsElement: tagsElement);
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"DetermineTagsPresent(): Exception parsing JSON for respository {repository.Uri} with error: {e.Message}";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ return false;
+ }
+
+ bool isTagMatch = DeterminePkgTagsSatisfyRequiredTags(pkgTags: pkgTags, requiredTags: tags);
+
+ return isTagMatch;
+ }
+
+ private string[] GetTagsFromJsonElement(JsonElement tagsElement)
+ {
+ List tagsFound = new List();
+ JsonElement[] pkgTagElements = tagsElement.EnumerateArray().ToArray();
+ foreach (JsonElement tagItem in pkgTagElements)
+ {
+ tagsFound.Add(tagItem.ToString().ToLower());
+ }
+
+ return tagsFound.ToArray();
+ }
+
+ private bool DeterminePkgTagsSatisfyRequiredTags(string[] pkgTags, string[] requiredTags)
+ {
+ bool isTagMatch = true;
+
+ foreach (string tag in requiredTags)
+ {
+ if (!pkgTags.Contains(tag.ToLower()))
+ {
+ isTagMatch = false;
+ break;
+ }
+ }
+
+ return isTagMatch;
+ }
+
+ private JsonElement[] GetPackageVersions(string packageBaseAddressUrl, string packageName, bool isNuGetRepo, out ExceptionDispatchInfo edi)
+ {
+ if (String.IsNullOrEmpty(packageBaseAddressUrl))
+ {
+ edi = ExceptionDispatchInfo.Capture(new ArgumentException($"GetPackageVersions(): Package Base URL cannot be null or empty"));
+ return new JsonElement[]{};
+ }
+
+ JsonElement[] pkgVersionsElement = GetJsonElementArr($"{packageBaseAddressUrl}{packageName.ToLower()}/index.json", versionsName, out edi);
+ if (edi != null)
+ {
+ return new JsonElement[]{};
+ }
+
+ return isNuGetRepo ? pkgVersionsElement.Reverse().ToArray() : pkgVersionsElement.ToArray();
+ }
+
+ private JsonElement[] GetJsonElementArr(string request, string propertyName, out ExceptionDispatchInfo edi)
+ {
+ JsonElement[] pkgsArr = new JsonElement[0];
+ try
+ {
+ string response = HttpRequestCall(request, out edi);
+ if (edi != null)
+ {
+ return new JsonElement[]{};
+ }
+
+ JsonDocument pkgsDom = JsonDocument.Parse(response);
+
+ pkgsDom.RootElement.TryGetProperty(propertyName, out JsonElement pkgs);
+
+ pkgsArr = pkgs.EnumerateArray().ToArray();
+ }
+ catch (Exception e)
+ {
+ string errMsg = $"Exception parsing JSON for respository {repository.Uri} with error: {e.Message}";
+ edi = ExceptionDispatchInfo.Capture(new JsonParsingException(errMsg));
+ }
+
+ return pkgsArr;
+ }
+
+ public static async Task SendV3RequestAsync(HttpRequestMessage message, HttpClient s_client)
+ {
+ string errMsg = "SendV3RequestAsync(): Error occured while trying to retrieve response: ";
+
+ try
+ {
+ HttpResponseMessage response = await s_client.SendAsync(message);
+ response.EnsureSuccessStatusCode();
+
+ var responseStr = await response.Content.ReadAsStringAsync();
+
+ return responseStr;
+ }
+ catch (HttpRequestException e)
+ {
+ throw new HttpRequestException(errMsg + e.Message);
+ }
+ catch (ArgumentNullException e)
+ {
+ throw new ArgumentNullException(errMsg + e.Message);
+ }
+ catch (InvalidOperationException e)
+ {
+ throw new InvalidOperationException(errMsg + e.Message);
+ }
+ }
+
+
+ public static async Task SendV3RequestForContentAsync(HttpRequestMessage message, HttpClient s_client)
+ {
+ string errMsg = "SendV3RequestForContentAsync(): Error occured while trying to retrieve response for content: ";
+
+ try
+ {
+ HttpResponseMessage response = await s_client.SendAsync(message);
+ response.EnsureSuccessStatusCode();
+ return response.Content;
+ }
+ catch (HttpRequestException e)
+ {
+ throw new HttpRequestException(errMsg + e.Message);
+ }
+ catch (ArgumentNullException e)
+ {
+ throw new ArgumentNullException(errMsg + e.Message);
+ }
+ catch (InvalidOperationException e)
+ {
+ throw new InvalidOperationException(errMsg + e.Message);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/test/FindPSResource.Tests.ps1 b/test/FindPSResource.Tests.ps1
deleted file mode 100644
index 53449b2d9..000000000
--- a/test/FindPSResource.Tests.ps1
+++ /dev/null
@@ -1,381 +0,0 @@
-# Copyright (c) Microsoft Corporation.
-# Licensed under the MIT License.
-
-Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
-
-Describe 'Test Find-PSResource for Module' {
-
- BeforeAll{
- $PSGalleryName = Get-PSGalleryName
- $NuGetGalleryName = Get-NuGetGalleryName
- $testModuleName = "test_module"
- $testScriptName = "test_script"
- $commandName = "Get-TargetResource"
- $dscResourceName = "SystemLocale"
- $parentModuleName = "SystemLocaleDsc"
- Get-NewPSResourceRepositoryFile
- Register-LocalRepos
- }
-
- AfterAll {
- Get-RevertPSResourceRepositoryFile
- }
-
- It "find Specific Module Resource by Name" {
- $specItem = Find-PSResource -Name $testModuleName
- $specItem.Name | Should -Be $testModuleName
- }
-
- It "should not find resource given nonexistant name" {
- $res = Find-PSResource -Name NonExistantModule
- $res | Should -BeNullOrEmpty
- }
-
- It "should not find any resources given names with invalid wildcard characters" {
- Find-PSResource -Name "Invalid?PkgName", "Invalid[PkgName" -ErrorVariable err -ErrorAction SilentlyContinue
- $err.Count | Should -Not -Be 0
- $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorFilteringNamesForUnsupportedWildcards,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
- }
-
- It "find resources when Name contains * from V2 endpoint repository (PowerShellGallery))" {
- $foundScript = $False
- $res = Find-PSResource -Name "AzureS*" -Repository $PSGalleryName
- $res.Count | Should -BeGreaterThan 1
- # should find Module and Script resources
- foreach ($item in $res)
- {
- if ($item.Type -eq "Script")
- {
- $foundScript = $true
- }
- }
-
- $foundScript | Should -BeTrue
- }
-
- # TODO: get working with local repo
- # It "should find all resources given Name that equals wildcard, '*'" {
- # $repoName = "psgettestlocal"
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule1" $repoName
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule2" $repoName
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule3" $repoName
-
- # $foundResources = Find-PSResource -Name "TestLocalModule1","TestLocalModule2","TestLocalModule3" -Repository $repoName
- # # TODO: wildcard search is not supported with local repositories, from NuGet protocol API side- ask about this.
- # # $foundResources = Find-PSResource -Name "*" -Repository $repoName
- # $foundResources.Count | Should -Not -Be 0
-
- # # Should find Module and Script resources but no prerelease resources
- # $foundResources | where-object Name -eq "TestLocalModule1" | Should -Not -BeNullOrEmpty -Because "TestLocalModule1 should exist in local repo"
- # $foundResources | where-object Name -eq "test_script" | Should -Not -BeNullOrEmpty -Because "TestLocalScript1 should exist in local repo"
- # $foundResources | where-object IsPrerelease -eq $true | Should -BeNullOrEmpty -Because "No prerelease resources should be returned"
- # }
-
- # # TODO: get working with local repo
- # It "should find all resources (including prerelease) given Name that equals wildcard, '*' and Prerelease parameter" {
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule1" $repoName
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule2" $repoName
- # Get-ModuleResourcePublishedToLocalRepoTestDrive "TestLocalModule3" $repoName
- # $foundResources = Find-PSResource -Name "*" -Prerelease -Repository $repoName
-
- # # Should find Module and Script resources inlcuding prerelease resources
- # $foundResources | where-object Name -eq "test_module" | Should -Not -BeNullOrEmpty -Because "test_module should exist in local repo"
- # $foundResources | where-object Name -eq "test_script" | Should -Not -BeNullOrEmpty -Because "test_script should exist in local repo"
- # $foundResources | where-object IsPrerelease -eq $true | Should -Not -BeNullOrEmpty -Because "Prerelease resources should be returned"
- # }
-
- It "find resource given Name from V3 endpoint repository (NuGetGallery)" {
- $res = Find-PSResource -Name "Serilog" -Repository $NuGetGalleryName
- $res.Count | Should -Be 1
- $res.Name | Should -Be "Serilog"
- $res.Repository | Should -Be $NuGetGalleryName
- }
-
- It "find resources when Name contains wildcard * from V3 endpoint repository" {
- $res = Find-PSResource -Name "Serilog*" -Repository $NuGetGalleryName
- $res.Count | Should -BeGreaterThan 1
- }
-
- $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match"},
- @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match without bracket syntax"},
- @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, exact range inclusive"},
- @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0.0"); Reason="validate version, exact range exclusive"},
- @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version exclusive"},
- @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version inclusive"},
- @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0.0"); Reason="validate version, maximum version exclusive"},
- @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, maximum version inclusive"},
- @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
- @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
-
- It "find resource when given Name to " -TestCases $testCases2{
- param($Version, $ExpectedVersions)
- $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName
- foreach ($item in $res) {
- $item.Name | Should -Be $testModuleName
- $ExpectedVersions | Should -Contain $item.Version
- }
- }
-
- It "not find resource with incorrectly formatted version such as " -TestCases @(
- @{Version='(1.0.0.0)'; Description="exclusive version (2.5.0.0)"},
- @{Version='[1-0-0-0]'; Description="version formatted with invalid delimiter"}
- ) {
- param($Version, $Description)
-
- $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName 2>$null
- $res | Should -BeNullOrEmpty
- }
-
- $testCases = @{Version='[1.*.0.0]'; Description="version with wilcard in middle"},
- @{Version='[*.0.0.0]'; Description="version with wilcard at start"},
- @{Version='[1.0.*.0]'; Description="version with wildcard at third digit"}
- @{Version='[1.0.0.*'; Description="version with wildcard at end"},
- @{Version='[1..0.0]'; Description="version with missing digit in middle"},
- @{Version='[1.0.0.]'; Description="version with missing digit at end"},
- @{Version='[1.0.0.0.0]'; Description="version with more than 4 digits"}
-
- It "not find resource and throw exception with incorrectly formatted version such as " -TestCases $testCases {
- param($Version, $Description)
-
- Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
- $err.Count | Should -Not -Be 0
- $err[0].FullyQualifiedErrorId | Should -BeExactly "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
- }
-
- It "find all versions of resource when given Name, Version not null --> '*'" {
- $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
- $res | ForEach-Object {
- $_.Name | Should -Be $testModuleName
- }
- $res.Count | Should -BeGreaterOrEqual 1
- }
-
- It "find resources when given Name with wildcard, Version not null --> '*'" {
- $res = Find-PSResource -Name "TestModuleWithDependency*" -Version "*" -Repository $PSGalleryName
- $moduleA = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyA"}
- $moduleA.Count | Should -BeGreaterOrEqual 3
- $moduleB = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyB"}
- $moduleB.Count | Should -BeGreaterOrEqual 2
- $moduleC = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyC"}
- $moduleC.Count | Should -BeGreaterOrEqual 3
- $moduleD = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyD"}
- $moduleD.Count | Should -BeGreaterOrEqual 2
- $moduleE = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyE"}
- $moduleE.Count | Should -BeGreaterOrEqual 1
- $moduleF = $res | Where-Object {$_.Name -eq "TestModuleWithDependencyF"}
- $moduleF.Count | Should -BeGreaterOrEqual 1
- }
-
- It "find resources when given Name with wildcard, Version range" {
- $res = Find-PSResource -Name "TestModuleWithDependency*" -Version "[1.0.0.0, 5.0.0.0]" -Repository $PSGalleryName
- foreach ($pkg in $res) {
- $pkg.Name | Should -Match "TestModuleWithDependency*"
- [System.Version]$pkg.Version -ge [System.Version]"1.0.0.0" -or [System.Version]$pkg.Version -le [System.Version]"5.0.0.0" | Should -Be $true
- }
- }
-
- It "find resource when given Name, Version param null" {
- $res = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
- $res.Name | Should -Be $testModuleName
- $res.Version | Should -Be "5.0.0.0"
- }
-
- It "find resource with latest (including prerelease) version given Prerelease parameter" {
- # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version
- $res = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
- $res.Version | Should -Be "5.0.0.0"
-
- $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName
- $resPrerelease.Version | Should -Be "5.2.5.0"
- $resPrerelease.Prerelease | Should -Be "alpha001"
- }
-
- It "find resources, including Prerelease version resources, when given Prerelease parameter" {
- $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
- $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
- $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count
- }
-
- It "find resource and its dependency resources with IncludeDependencies parameter" {
- $resWithoutDependencies = Find-PSResource -Name "TestModuleWithDependencyE" -Repository $PSGalleryName
- $resWithoutDependencies.Count | Should -Be 1
- $resWithoutDependencies.Name | Should -Be "TestModuleWithDependencyE"
-
- # TestModuleWithDependencyE has the following dependencies:
- # TestModuleWithDependencyC <= 1.0.0.0
- # TestModuleWithDependencyB >= 1.0.0.0
- # TestModuleWithDependencyD <= 1.0.0.0
-
- $resWithDependencies = Find-PSResource -Name "TestModuleWithDependencyE" -IncludeDependencies -Repository $PSGalleryName
- $resWithDependencies.Count | Should -BeGreaterThan $resWithoutDependencies.Count
-
- $foundParentPkgE = $false
- $foundDepB = $false
- $foundDepBCorrectVersion = $false
- $foundDepC = $false
- $foundDepCCorrectVersion = $false
- $foundDepD = $false
- $foundDepDCorrectVersion = $false
- foreach ($pkg in $resWithDependencies)
- {
- if ($pkg.Name -eq "TestModuleWithDependencyE")
- {
- $foundParentPkgE = $true
- }
- elseif ($pkg.Name -eq "TestModuleWithDependencyC")
- {
- $foundDepC = $true
- $foundDepCCorrectVersion = [System.Version]$pkg.Version -le [System.Version]"1.0.0.0"
- }
- elseif ($pkg.Name -eq "TestModuleWithDependencyB")
- {
- $foundDepB = $true
- $foundDepBCorrectVersion = [System.Version]$pkg.Version -ge [System.Version]"3.0.0.0"
- }
- elseif ($pkg.Name -eq "TestModuleWithDependencyD")
- {
- $foundDepD = $true
- $foundDepDCorrectVersion = [System.Version]$pkg.Version -le [System.Version]"1.0.0.0"
- }
- }
-
- $foundParentPkgE | Should -Be $true
- $foundDepC | Should -Be $true
- $foundDepCCorrectVersion | Should -Be $true
- $foundDepB | Should -Be $true
- $foundDepBCorrectVersion | Should -Be $true
- $foundDepD | Should -Be $true
- $foundDepDCorrectVersion | Should -Be $true
- }
-
- It "find resource of Type script or module from PSGallery, when no Type parameter provided" {
- $resScript = Find-PSResource -Name $testScriptName -Repository $PSGalleryName
- $resScript.Name | Should -Be $testScriptName
- $resScriptType = Out-String -InputObject $resScript.Type
- $resScriptType.Replace(",", " ").Split() | Should -Contain "Script"
-
- $resModule = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
- $resModule.Name | Should -Be $testModuleName
- $resModuleType = Out-String -InputObject $resModule.Type
- $resModuleType.Replace(",", " ").Split() | Should -Contain "Module"
- }
-
- It "find resource of Type Script from PSGallery, when Type Script specified" {
- $resScript = Find-PSResource -Name $testScriptName -Repository $PSGalleryName -Type "Script"
- $resScript.Name | Should -Be $testScriptName
- $resScript.Repository | Should -Be "PSGalleryScripts"
- $resScriptType = Out-String -InputObject $resScript.Type
- $resScriptType.Replace(",", " ").Split() | Should -Contain "Script"
- }
-
- It "find resource of Type Command from PSGallery, when Type Command specified" {
- $resources = Find-PSResource -Name "AzureS*" -Repository $PSGalleryName -Type "Command"
- foreach ($item in $resources) {
- $resType = Out-String -InputObject $item.Type
- $resType.Replace(",", " ").Split() | Should -Contain "Command"
- }
- }
-
- It "find all resources of Type Module when Type parameter set is used" -Skip {
- $foundScript = $False
- $res = Find-PSResource -Name "test*" -Type Module -Repository $PSGalleryName
- $res.Count | Should -BeGreaterThan 1
- foreach ($item in $res) {
- if ($item.Type -eq "Script")
- {
- $foundScript = $True
- }
- }
-
- $foundScript | Should -Be $False
- }
-
- It "find resources given Tag parameter" {
- $resWithEitherExpectedTag = @("NetworkingDsc", "DSCR_FileContent", "SS.PowerShell")
- $res = Find-PSResource -Name "NetworkingDsc", "HPCMSL", "DSCR_FileContent", "SS.PowerShell", "PowerShellGet" -Tag "Dsc", "json" -Repository $PSGalleryName
- foreach ($item in $res) {
- $resWithEitherExpectedTag | Should -Contain $item.Name
- }
- }
-
- It "find all resources with specified tag given Tag property" {
- $foundTestModule = $False
- $foundTestScript = $False
- $tagToFind = "Tag2"
- $res = Find-PSResource -Tag $tagToFind -Repository $PSGalleryName
- foreach ($item in $res) {
- $item.Tags -contains $tagToFind | Should -Be $True
-
- if ($item.Name -eq $testModuleName)
- {
- $foundTestModule = $True
- }
-
- if ($item.Name -eq $testScriptName)
- {
- $foundTestScript = $True
- }
- }
-
- $foundTestModule | Should -Be $True
- $foundTestScript | Should -Be $True
- }
-
- It "find resource in local repository given Repository parameter" {
- $publishModuleName = "TestFindModule"
- $repoName = "psgettestlocal"
- Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleName $repoName
-
- $res = Find-PSResource -Name $publishModuleName -Repository $repoName
- $res | Should -Not -BeNullOrEmpty
- $res.Name | Should -Be $publishModuleName
- $res.Repository | Should -Be $repoName
- }
-
- It "find Resource given repository parameter, where resource exists in multiple local repos" {
- $moduleName = "test_local_mod"
- $repoHigherPriorityRanking = "psgettestlocal"
- $repoLowerPriorityRanking = "psgettestlocal2"
-
- Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $repoHigherPriorityRanking
- Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $repoLowerPriorityRanking
-
- $res = Find-PSResource -Name $moduleName
- $res.Repository | Should -Be $repoHigherPriorityRanking
-
- $resNonDefault = Find-PSResource -Name $moduleName -Repository $repoLowerPriorityRanking
- $resNonDefault.Repository | Should -Be $repoLowerPriorityRanking
- }
-
- # # Skip test for now because it takes too run (132.24 sec)
- # It "find resource given CommandName (CommandNameParameterSet)" -Skip {
- # $res = Find-PSResource -CommandName $commandName -Repository $PSGalleryName
- # foreach ($item in $res) {
- # $item.Name | Should -Be $commandName
- # $item.ParentResource.Includes.Command | Should -Contain $commandName
- # }
- # }
-
- It "find resource given CommandName and ModuleName (CommandNameParameterSet)" {
- $res = Find-PSResource -CommandName $commandName -ModuleName $parentModuleName -Repository $PSGalleryName
- $res.Name | Should -Be $commandName
- $res.ParentResource.Name | Should -Be $parentModuleName
- $res.ParentResource.Includes.Command | Should -Contain $commandName
- }
-
- # Skip test for now because it takes too long to run (> 60 sec)
- # It "find resource given DSCResourceName (DSCResourceNameParameterSet)" -Skip {
- # $res = Find-PSResource -DscResourceName $dscResourceName -Repository $PSGalleryName
- # foreach ($item in $res) {
- # $item.Name | Should -Be $dscResourceName
- # $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName
- # }
- # }
-
- It "find resource given DscResourceName and ModuleName (DSCResourceNameParameterSet)" {
- $res = Find-PSResource -DscResourceName $dscResourceName -ModuleName $parentModuleName -Repository $PSGalleryName
- $res.Name | Should -Be $dscResourceName
- $res.ParentResource.Name | Should -Be $parentModuleName
- $res.ParentResource.Includes.DscResource | Should -Contain $dscResourceName
- }
-}
diff --git a/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1
new file mode 100644
index 000000000..65c7e0fff
--- /dev/null
+++ b/test/FindPSResourceTests/FindPSResourceLocal.Tests.ps1
@@ -0,0 +1,208 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Find-PSResource for Module' {
+
+ BeforeAll{
+ $localRepo = "psgettestlocal"
+ $testModuleName = "test_local_mod"
+ $testModuleName2 = "test_local_mod2"
+ $commandName = "cmd1"
+ $dscResourceName = "dsc1"
+ $cmdName = "PSCommand_$commandName"
+ $dscName = "PSDscResource_$dscResourceName"
+ $prereleaseLabel = ""
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+
+ $tags = @("Test", "Tag2", $cmdName, $dscName)
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $testModuleName $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $testModuleName $localRepo "3.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $testModuleName $localRepo "5.0.0" $prereleaseLabel $tags
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $testModuleName2 $localRepo "5.0.0" $prereleaseLabel $tags
+
+ $prereleaseLabel = "alpha001"
+ $params = @{
+ moduleName = $testModuleName
+ repoName = $localRepo
+ packageVersion = "5.2.5"
+ prereleaseLabel = $prereleaseLabel
+ tags = $tags
+ }
+ Get-ModuleResourcePublishedToLocalRepoTestDrive @params
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "find resource given specific Name, Version null" {
+ # FindName()
+ $res = Find-PSResource -Name $testModuleName -Repository $localRepo
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ }
+
+ It "should not find resource given nonexistant Name" {
+ $res = Find-PSResource -Name NonExistantModule -Repository $localRepo
+ $res | Should -BeNullOrEmpty
+ }
+
+ # It "find resource(s) given wildcard Name" {
+ # # FindNameGlobbing
+ # $res = Find-PSResource -Name "test_local_*" -Repository $localRepo
+ # $res.Count | Should -BeGreaterThan 1
+ # }
+
+ $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match"},
+ @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match without bracket syntax"},
+ @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, exact range inclusive"},
+ @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0.0"); Reason="validate version, exact range exclusive"},
+ @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version exclusive"},
+ @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version inclusive"},
+ @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0.0"); Reason="validate version, maximum version exclusive"},
+ @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, maximum version inclusive"},
+ @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
+ @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
+
+ It "find resource when given Name to " -TestCases $testCases2{
+ # FindVersionGlobbing()
+ param($Version, $ExpectedVersions)
+ $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $localRepo
+ foreach ($item in $res) {
+ $item.Name | Should -Be $testModuleName
+ $ExpectedVersions | Should -Contain $item.Version
+ }
+ }
+
+ It "find all versions of resource when given specific Name, Version not null --> '*'" {
+ # FindVersionGlobbing()
+ $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo
+ $res | ForEach-Object {
+ $_.Name | Should -Be $testModuleName
+ }
+
+ $res.Count | Should -BeGreaterOrEqual 1
+ }
+
+ It "find resource with latest (including prerelease) version given Prerelease parameter" {
+ # FindName()
+ # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version
+ $res = Find-PSResource -Name $testModuleName -Repository $localRepo
+ $res.Version | Should -Be "5.0.0.0"
+
+ $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $localRepo
+ $resPrerelease.Version | Should -Be "5.2.5.0"
+ $resPrerelease.Prerelease | Should -Be "alpha001"
+ }
+
+ It "find resources, including Prerelease version resources, when given Prerelease parameter" {
+ # FindVersionGlobbing()
+ $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo
+ $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $localRepo
+ $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count
+ }
+
+ It "find resource that satisfies given Name and Tag property (single tag)" {
+ # FindNameWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name and Tag are not both satisfied (single tag)" {
+ # FindNameWithTag
+ $requiredTag = "Windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $localRepo
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource that satisfies given Name and Tag property (multiple tags)" {
+ # FindNameWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $localRepo
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "test"
+ $nameWithWildcard = "test_local_mod*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTag -Repository $localRepo
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTag
+ }
+ }
+
+ It "should not find resources if both Name pattern and Tags are not satisfied (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name "test_module*" -Tag $requiredTag -Repository $localRepo
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (multiple tags)" {
+ # FindNameGlobbingWithTag()
+ $requiredTags = @("test", "Tag2")
+ $nameWithWildcard = "test_local_mod*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTags -Repository $localRepo
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTags[0]
+ $pkg.Tags | Should -Contain $requiredTags[1]
+ }
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name, Version and Tag property are not all satisfied (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $localRepo
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (multiple tags)" {
+ # FindVersionWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $localRepo
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+
+ }
+
+ It "find resource given CommandName" {
+ $res = Find-PSResource -CommandName $commandName -Repository $localRepo
+ foreach ($item in $res) {
+ $item.Names | Should -Be $commandName
+ $item.ParentResource.Includes.Command | Should -Contain $commandName
+ }
+ }
+
+ It "find resource given DscResourceName" {
+ $res = Find-PSResource -DscResourceName $dscResourceName -Repository $localRepo
+ foreach ($item in $res) {
+ $item.Names | Should -Be $dscResourceName
+ $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName
+ }
+ }
+}
diff --git a/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1
new file mode 100644
index 000000000..6d479fc95
--- /dev/null
+++ b/test/FindPSResourceTests/FindPSResourceV2Server.Tests.ps1
@@ -0,0 +1,352 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Find-PSResource for V2 Server Protocol' {
+
+ BeforeAll{
+ $PSGalleryName = Get-PSGalleryName
+ $testModuleName = "test_module"
+ $testScriptName = "test_script"
+ $commandName = "Get-TargetResource"
+ $dscResourceName = "SystemLocale"
+ $parentModuleName = "SystemLocaleDsc"
+ Get-NewPSResourceRepositoryFile
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "find resource given specific Name, Version null" {
+ # FindName()
+ $res = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ }
+
+ It "should not find resource given nonexistant Name" {
+ $res = Find-PSResource -Name NonExistantModule -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameResponseConversionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource(s) given wildcard Name" {
+ # FindNameGlobbing
+ $foundScript = $False
+ $res = Find-PSResource -Name "test_*" -Repository $PSGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ # should find Module and Script resources
+ foreach ($item in $res)
+ {
+ if ($item.Type -eq "Script")
+ {
+ $foundScript = $true
+ }
+ }
+
+ $foundScript | Should -BeTrue
+ }
+
+ $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match"},
+ @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0.0"); Reason="validate version, exact match without bracket syntax"},
+ @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, exact range inclusive"},
+ @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0.0"); Reason="validate version, exact range exclusive"},
+ @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version exclusive"},
+ @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0", "5.0.0.0"); Reason="validate version, minimum version inclusive"},
+ @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0.0"); Reason="validate version, maximum version exclusive"},
+ @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, maximum version inclusive"},
+ @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
+ @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0.0", "5.0.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
+
+ It "find resource when given Name to " -TestCases $testCases2{
+ # FindVersionGlobbing()
+ param($Version, $ExpectedVersions)
+ $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName
+ foreach ($item in $res) {
+ $item.Name | Should -Be $testModuleName
+ $ExpectedVersions | Should -Contain $item.Version
+ }
+ }
+
+ It "find all versions of resource when given specific Name, Version not null --> '*'" {
+ # FindVersionGlobbing()
+ $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
+ $res | ForEach-Object {
+ $_.Name | Should -Be $testModuleName
+ }
+
+ $res.Count | Should -BeGreaterOrEqual 1
+ }
+
+ It "find resource with latest (including prerelease) version given Prerelease parameter" {
+ # FindName()
+ # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version
+ $res = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
+ $res.Version | Should -Be "5.0.0.0"
+
+ $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName
+ $resPrerelease.Version | Should -Be "5.2.5"
+ $resPrerelease.Prerelease | Should -Be "alpha001"
+ }
+
+ It "find resources, including Prerelease version resources, when given Prerelease parameter" {
+ # FindVersionGlobbing()
+ $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
+ $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName
+ $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count
+ }
+
+ It "find resource and its dependency resources with IncludeDependencies parameter" {
+ # FindName() with deps
+ $resWithoutDependencies = Find-PSResource -Name "TestModuleWithDependencyE" -Repository $PSGalleryName
+ $resWithoutDependencies.Count | Should -Be 1
+ $resWithoutDependencies.Name | Should -Be "TestModuleWithDependencyE"
+
+ # TestModuleWithDependencyE has the following dependencies:
+ # TestModuleWithDependencyC <= 1.0.0.0
+ # TestModuleWithDependencyB >= 1.0.0.0
+ # TestModuleWithDependencyD <= 1.0.0.0
+
+ $resWithDependencies = Find-PSResource -Name "TestModuleWithDependencyE" -IncludeDependencies -Repository $PSGalleryName
+ $resWithDependencies.Count | Should -BeGreaterThan $resWithoutDependencies.Count
+
+ $foundParentPkgE = $false
+ $foundDepB = $false
+ $foundDepBCorrectVersion = $false
+ $foundDepC = $false
+ $foundDepCCorrectVersion = $false
+ $foundDepD = $false
+ $foundDepDCorrectVersion = $false
+ foreach ($pkg in $resWithDependencies)
+ {
+ if ($pkg.Name -eq "TestModuleWithDependencyE")
+ {
+ $foundParentPkgE = $true
+ }
+ elseif ($pkg.Name -eq "TestModuleWithDependencyC")
+ {
+ $foundDepC = $true
+ $foundDepCCorrectVersion = [System.Version]$pkg.Version -le [System.Version]"1.0"
+ }
+ elseif ($pkg.Name -eq "TestModuleWithDependencyB")
+ {
+ $foundDepB = $true
+ $foundDepBCorrectVersion = [System.Version]$pkg.Version -ge [System.Version]"3.0"
+ }
+ elseif ($pkg.Name -eq "TestModuleWithDependencyD")
+ {
+ $foundDepD = $true
+ $foundDepDCorrectVersion = [System.Version]$pkg.Version -le [System.Version]"1.0"
+ }
+ }
+
+ $foundParentPkgE | Should -Be $true
+ $foundDepC | Should -Be $true
+ $foundDepCCorrectVersion | Should -Be $true
+ $foundDepB | Should -Be $true
+ $foundDepBCorrectVersion | Should -Be $true
+ $foundDepD | Should -Be $true
+ $foundDepDCorrectVersion | Should -Be $true
+ }
+
+ It "find resource of Type script or module from PSGallery, when no Type parameter provided" {
+ # FindName() script
+ $resScript = Find-PSResource -Name $testScriptName -Repository $PSGalleryName
+ $resScript.Name | Should -Be $testScriptName
+ $resScriptType = Out-String -InputObject $resScript.Type
+ $resScriptType.Replace(",", " ").Split() | Should -Contain "Script"
+
+ $resModule = Find-PSResource -Name $testModuleName -Repository $PSGalleryName
+ $resModule.Name | Should -Be $testModuleName
+ $resModuleType = Out-String -InputObject $resModule.Type
+ $resModuleType.Replace(",", " ").Split() | Should -Contain "Module"
+ }
+
+ It "find resource of Type Script from PSGallery, when Type Script specified" {
+ # FindName() Type script
+ $resScript = Find-PSResource -Name $testScriptName -Repository $PSGalleryName -Type "Script"
+ $resScript.Name | Should -Be $testScriptName
+ $resScriptType = Out-String -InputObject $resScript.Type
+ $resScriptType.Replace(",", " ").Split() | Should -Contain "Script"
+ }
+
+ It "find all resources of Type Module when Type parameter set is used" {
+ $foundScript = $False
+ $res = Find-PSResource -Name "test*" -Type Module -Repository $PSGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($item in $res) {
+ if ($item.Type -eq "Script")
+ {
+ $foundScript = $True
+ }
+ }
+
+ $foundScript | Should -Be $False
+ }
+
+ # It "find resources only with Tag parameter" {
+ # $resWithEitherExpectedTag = @("NetworkingDsc", "DSCR_FileContent", "SS.PowerShell")
+ # $res = Find-PSResource -Name "NetworkingDsc", "HPCMSL", "DSCR_FileContent", "SS.PowerShell", "PowerShellGet" -Tag "Dsc", "json" -Repository $PSGalleryName
+ # foreach ($item in $res) {
+ # $resWithEitherExpectedTag | Should -Contain $item.Name
+ # }
+ # }
+
+ It "find resource that satisfies given Name and Tag property (single tag)" {
+ # FindNameWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $PSGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name and Tag are not both satisfied (single tag)" {
+ # FindNameWithTag
+ $requiredTag = "Windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameResponseConversionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find resource that satisfies given Name and Tag property (multiple tags)" {
+ # FindNameWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $PSGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+ }
+
+ It "should not find resource if Name and Tag are not both satisfied (multiple tag)" {
+ # FindNameWithTag
+ $requiredTags = @("test", "Windows") # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameResponseConversionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "test"
+ $nameWithWildcard = "test_module*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTag -Repository $PSGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTag
+ }
+ }
+
+ It "should not find resources if both Name pattern and Tags are not satisfied (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name "test_module*" -Tag $requiredTag -Repository $PSGalleryName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (multiple tags)" {
+ # FindNameGlobbingWithTag()
+ $requiredTags = @("test", "Tag2")
+ $nameWithWildcard = "test_module*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTags -Repository $PSGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTags[0]
+ $pkg.Tags | Should -Contain $requiredTags[1]
+ }
+ }
+
+ It "should not find resources if both Name pattern and Tags are not satisfied (multiple tags)" {
+ # FindNameGlobbingWithTag() # tag "windows" is not present for test_module package
+ $requiredTags = @("test", "windows")
+ $res = Find-PSResource -Name "test_module*" -Tag $requiredTags -Repository $PSGalleryName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $PSGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name, Version and Tag property are not all satisfied (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindVersionResponseConversionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (multiple tags)" {
+ # FindVersionWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $PSGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0.0"
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+
+ }
+
+ It "should not find resource if Name, Version and Tag property are not all satisfied (multiple tags)" {
+ # FindVersionWithTag()
+ $requiredTags = @("test", "windows")
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindVersionResponseConversionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ # It "find all resources with specified tag given Tag property" {
+ # # FindTag()
+ # $foundTestModule = $False
+ # $foundTestScript = $False
+ # $tagToFind = "Tag2"
+ # $res = Find-PSResource -Tag $tagToFind -Repository $PSGalleryName
+ # foreach ($item in $res) {
+ # $item.Tags -contains $tagToFind | Should -Be $True
+
+ # if ($item.Name -eq $testModuleName)
+ # {
+ # $foundTestModule = $True
+ # }
+
+ # if ($item.Name -eq $testScriptName)
+ # {
+ # $foundTestScript = $True
+ # }
+ # }
+
+ # $foundTestModule | Should -Be $True
+ # $foundTestScript | Should -Be $True
+ # }
+
+ It "find resource given CommandName" {
+ $res = Find-PSResource -CommandName $commandName -Repository $PSGalleryName
+ foreach ($item in $res) {
+ $item.Names | Should -Be $commandName
+ $item.ParentResource.Includes.Command | Should -Contain $commandName
+ }
+ }
+
+ It "find resource given DscResourceName" {
+ $res = Find-PSResource -DscResourceName $dscResourceName -Repository $PSGalleryName
+ foreach ($item in $res) {
+ $item.Names | Should -Be $dscResourceName
+ $item.ParentResource.Includes.DscResource | Should -Contain $dscResourceName
+ }
+ }
+}
diff --git a/test/FindPSResourceTests/FindPSResourceV3Server.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceV3Server.Tests.ps1
new file mode 100644
index 000000000..b87768784
--- /dev/null
+++ b/test/FindPSResourceTests/FindPSResourceV3Server.Tests.ps1
@@ -0,0 +1,267 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Find-PSResource for V2 Server Protocol' {
+
+ BeforeAll{
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $testModuleName = "test_module"
+ Get-NewPSResourceRepositoryFile
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "find resource given specific Name, Version null" {
+ # FindName()
+ $res = Find-PSResource -Name $testModuleName -Repository $NuGetGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0"
+ }
+
+ It "should not find resource given nonexistant Name" {
+ $res = Find-PSResource -Name NonExistantModule -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource(s) given wildcard Name" {
+ # FindNameGlobbing
+ $wildcardName = "test_module*"
+ $res = Find-PSResource -Name $wildcardName -Repository $NuGetGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($item in $res)
+ {
+ $item.Name | Should -BeLike $wildcardName
+ }
+ }
+
+ $testCases2 = @{Version="[5.0.0.0]"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match"},
+ @{Version="5.0.0.0"; ExpectedVersions=@("5.0.0"); Reason="validate version, exact match without bracket syntax"},
+ @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range inclusive"},
+ @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("3.0.0"); Reason="validate version, exact range exclusive"},
+ @{Version="(1.0.0.0,)"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, minimum version exclusive"},
+ @{Version="[1.0.0.0,)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, minimum version inclusive"},
+ @{Version="(,3.0.0.0)"; ExpectedVersions=@("1.0.0"); Reason="validate version, maximum version exclusive"},
+ @{Version="(,3.0.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, maximum version inclusive"},
+ @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
+ @{Version="(1.0.0.0, 5.0.0.0]"; ExpectedVersions=@("3.0.0", "5.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
+
+ It "find resource when given Name to " -TestCases $testCases2{
+ # FindVersionGlobbing()
+ param($Version, $ExpectedVersions)
+ $res = Find-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName
+ foreach ($item in $res) {
+ $item.Name | Should -Be $testModuleName
+ $ExpectedVersions | Should -Contain $item.Version
+ }
+ }
+
+ It "find all versions of resource when given specific Name, Version not null --> '*'" {
+ # FindVersionGlobbing()
+ $res = Find-PSResource -Name $testModuleName -Version "*" -Repository $NuGetGalleryName
+ $res | ForEach-Object {
+ $_.Name | Should -Be $testModuleName
+ }
+
+ $res.Count | Should -BeGreaterOrEqual 1
+ }
+
+ It "find resource with latest (including prerelease) version given Prerelease parameter" {
+ # FindName()
+ # test_module resource's latest version is a prerelease version, before that it has a non-prerelease version
+ $res = Find-PSResource -Name $testModuleName -Repository $NuGetGalleryName
+ $res.Version | Should -Be "5.0.0"
+
+ $resPrerelease = Find-PSResource -Name $testModuleName -Prerelease -Repository $NuGetGalleryName
+ $resPrerelease.Version | Should -Be "5.2.5"
+ $resPrerelease.Prerelease | Should -Be "alpha001"
+ }
+
+ It "find resources, including Prerelease version resources, when given Prerelease parameter" {
+ # FindVersionGlobbing()
+ $resWithoutPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $NuGetGalleryName
+ $resWithPrerelease = Find-PSResource -Name $testModuleName -Version "*" -Repository $NuGetGalleryName
+ $resWithPrerelease.Count | Should -BeGreaterOrEqual $resWithoutPrerelease.Count
+ }
+
+ It "find resource and its dependency resources with IncludeDependencies parameter" {
+ # find with dependencies is not yet supported for V3, so this should only install parent package
+ $pkg = Find-PSResource -Name "TestModuleWithDependencyE" -IncludeDependencies -Repository $NuGetGalleryName
+ $pkg.Count | Should -Be 1
+ $pkg.Name | Should -Be "TestModuleWithDependencyE"
+ }
+
+ # It "find resources only with Tag parameter" {
+ # $resWithEitherExpectedTag = @("NetworkingDsc", "DSCR_FileContent", "SS.PowerShell")
+ # $res = Find-PSResource -Name "NetworkingDsc", "HPCMSL", "DSCR_FileContent", "SS.PowerShell", "PowerShellGet" -Tag "Dsc", "json" -Repository $NuGetGalleryName
+ # foreach ($item in $res) {
+ # $resWithEitherExpectedTag | Should -Contain $item.Name
+ # }
+ # }
+
+ It "find resource that satisfies given Name and Tag property (single tag)" {
+ # FindNameWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $NuGetGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name and Tag are not both satisfied (single tag)" {
+ # FindNameWithTag
+ $requiredTag = "Windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTag -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find resource that satisfies given Name and Tag property (multiple tags)" {
+ # FindNameWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $NuGetGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+ }
+
+ It "should not find resource if Name and Tag are not both satisfied (multiple tag)" {
+ # FindNameWithTag
+ $requiredTags = @("test", "Windows") # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Tag $requiredTags -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindNameFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "test"
+ $nameWithWildcard = "test_module*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTag -Repository $NuGetGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTag
+ }
+ }
+
+ It "should not find resources if both Name pattern and Tags are not satisfied (single tag)" {
+ # FindNameGlobbingWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name "test_module*" -Tag $requiredTag -Repository $NuGetGalleryName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find all resources that satisfy Name pattern and have specified Tag (multiple tags)" {
+ # FindNameGlobbingWithTag()
+ $requiredTags = @("test", "Tag2")
+ $nameWithWildcard = "test_module*"
+ $res = Find-PSResource -Name $nameWithWildcard -Tag $requiredTags -Repository $NuGetGalleryName
+ $res.Count | Should -BeGreaterThan 1
+ foreach ($pkg in $res)
+ {
+ $pkg.Name | Should -BeLike $nameWithWildcard
+ $pkg.Tags | Should -Contain $requiredTags[0]
+ $pkg.Tags | Should -Contain $requiredTags[1]
+ }
+ }
+
+ It "should not find resources if both Name pattern and Tags are not satisfied (multiple tags)" {
+ # FindNameGlobbingWithTag() # tag "windows" is not present for test_module package
+ $requiredTags = @("test", "windows")
+ $res = Find-PSResource -Name "test_module*" -Tag $requiredTags -Repository $NuGetGalleryName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "test"
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $NuGetGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0"
+ $res.Tags | Should -Contain $requiredTag
+ }
+
+ It "should not find resource if Name, Version and Tag property are not all satisfied (single tag)" {
+ # FindVersionWithTag()
+ $requiredTag = "windows" # tag "windows" is not present for test_module package
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTag -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindVersionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "find resource that satisfies given Name, Version and Tag property (multiple tags)" {
+ # FindVersionWithTag()
+ $requiredTags = @("test", "Tag2")
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $NuGetGalleryName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "5.0.0"
+ $res.Tags | Should -Contain $requiredTags[0]
+ $res.Tags | Should -Contain $requiredTags[1]
+
+ }
+
+ It "should not find resource if Name, Version and Tag property are not all satisfied (multiple tags)" {
+ # FindVersionWithTag()
+ $requiredTags = @("test", "windows")
+ $res = Find-PSResource -Name $testModuleName -Version "5.0.0.0" -Tag $requiredTags -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindVersionFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ # It "find all resources with specified tag given Tag property" {
+ # # FindTag()
+ # $foundTestModule = $False
+ # $foundTestScript = $False
+ # $tagToFind = "Tag2"
+ # $res = Find-PSResource -Tag $tagToFind -Repository $NuGetGalleryName
+ # foreach ($item in $res) {
+ # $item.Tags -contains $tagToFind | Should -Be $True
+
+ # if ($item.Name -eq $testModuleName)
+ # {
+ # $foundTestModule = $True
+ # }
+
+ # if ($item.Name -eq $testScriptName)
+ # {
+ # $foundTestScript = $True
+ # }
+ # }
+
+ # $foundTestModule | Should -Be $True
+ # $foundTestScript | Should -Be $True
+ # }
+
+ It "should not find resource given CommandName" {
+ $res = Find-PSResource -CommandName "command" -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindCommandOrDSCResourceFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "should not find resource given DscResourceName" {
+ $res = Find-PSResource -DscResourceName "dscResource" -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindCommandOrDSCResourceFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+ }
+
+ It "should not find all resources given Name '*'" {
+ $res = Find-PSResource -Name "*" -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "FindAllFail,Microsoft.PowerShell.PowerShellGet.Cmdlets.FindPSResource"
+
+ }
+}
diff --git a/test/InstallPSResource.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResource.Tests.ps1
similarity index 96%
rename from test/InstallPSResource.Tests.ps1
rename to test/InstallPSResourceTests/InstallPSResource.Tests.ps1
index e6106c869..a744d8e4b 100644
--- a/test/InstallPSResource.Tests.ps1
+++ b/test/InstallPSResourceTests/InstallPSResource.Tests.ps1
@@ -1,570 +1,562 @@
-# Copyright (c) Microsoft Corporation.
-# Licensed under the MIT License.
-
-$ProgressPreference = "SilentlyContinue"
-Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
-
-Describe 'Test Install-PSResource for Module' {
-
- BeforeAll {
- $PSGalleryName = Get-PSGalleryName
- $PSGalleryUri = Get-PSGalleryLocation
- $NuGetGalleryName = Get-NuGetGalleryName
- $testModuleName = "test_module"
- $testModuleName2 = "TestModule99"
- $testScriptName = "test_script"
- $PackageManagement = "PackageManagement"
- $RequiredResourceJSONFileName = "TestRequiredResourceFile.json"
- $RequiredResourcePSD1FileName = "TestRequiredResourceFile.psd1"
- Get-NewPSResourceRepositoryFile
- Register-LocalRepos
- }
-
- AfterEach {
- Uninstall-PSResource "test_module", "test_module2", "test_script", "TestModule99", "testModuleWithlicense", "TestFindModule","ClobberTestModule1", "ClobberTestModule2", "PackageManagement" -SkipDependencyCheck -ErrorAction SilentlyContinue
- }
-
- AfterAll {
- Get-RevertPSResourceRepositoryFile
- }
-
- $testCases = @{Name="*"; ErrorId="NameContainsWildcard"},
- @{Name="Test_Module*"; ErrorId="NameContainsWildcard"},
- @{Name="Test?Module","Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}
-
- It "Should not install resource with wildcard in name" -TestCases $testCases {
- param($Name, $ErrorId)
- Install-PSResource -Name $Name -ErrorVariable err -ErrorAction SilentlyContinue
- $err.Count | Should -Not -Be 0
- $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- It "Install specific module resource by name" {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- }
-
- It "Install specific script resource by name" {
- Install-PSResource -Name $testScriptName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testScriptName
- $pkg.Name | Should -Be $testScriptName
- $pkg.Version | Should -Be "3.5.0.0"
- }
-
- It "Install multiple resources by name" {
- $pkgNames = @($testModuleName,$testModuleName2)
- Install-PSResource -Name $pkgNames -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $pkgNames
- $pkg.Name | Should -Be $pkgNames
- }
-
- It "Should not install resource given nonexistant name" {
- Install-PSResource -Name "NonExistantModule" -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
- $pkg = Get-PSResource "NonExistantModule"
- $pkg.Name | Should -BeNullOrEmpty
- $err.Count | Should -Not -Be 0
- $err[0].FullyQualifiedErrorId | Should -BeExactly "ResourceNotFoundError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- # Do some version testing, but Find-PSResource should be doing thorough testing
- It "Should install resource given name and exact version" {
- Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "1.0.0.0"
- }
-
- It "Should install resource given name and exact version with bracket syntax" {
- Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "1.0.0.0"
- }
-
- It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" {
- Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- }
-
- It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" {
- Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "3.0.0.0"
- }
-
- # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw
- It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
- $Version = "(1.0.0.0)"
- try {
- Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue
- }
- catch
- {}
- $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
-
- $res = Get-PSResource $testModuleName
- $res | Should -BeNullOrEmpty
- }
-
- It "Should not install resource with incorrectly formatted version such as version formatted with invalid delimiter [1-0-0-0]" {
- $Version="[1-0-0-0]"
- try {
- Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue
- }
- catch
- {}
- $Error[0].FullyQualifiedErrorId | Should -be "ResourceNotFoundError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
-
- $res = Get-PSResource $testModuleName
- $res | Should -BeNullOrEmpty
- }
-
- It "Install resource when given Name, Version '*', should install the latest version" {
- Install-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- }
-
- It "Install resource with latest (including prerelease) version given Prerelease parameter" {
- Install-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.2.5"
- $pkg.Prerelease | Should -Be "alpha001"
- }
-
- It "Install a module with a dependency" {
- Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
- Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
-
- $pkg = Get-PSResource "TestModuleWithDependencyC"
- $pkg.Name | Should -Be "TestModuleWithDependencyC"
- $pkg.Version | Should -Be "1.0.0.0"
-
- $pkg = Get-PSResource "TestModuleWithDependencyB"
- $pkg.Name | Should -Be "TestModuleWithDependencyB"
- $pkg.Version | Should -Be "3.0.0.0"
-
- $pkg = Get-PSResource "TestModuleWithDependencyD"
- $pkg.Name | Should -Be "TestModuleWithDependencyD"
- $pkg.Version | Should -Be "1.0.0.0"
- }
-
- It "Install a module with a dependency and skip installing the dependency" {
- Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
- Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource "TestModuleWithDependencyC"
- $pkg.Name | Should -Be "TestModuleWithDependencyC"
- $pkg.Version | Should -Be "1.0.0.0"
-
- $pkg = Get-PSResource "TestModuleWithDependencyB", "TestModuleWithDependencyD"
- $pkg | Should -BeNullOrEmpty
- }
-
- It "Install resource via InputObject by piping from Find-PSresource" {
- Find-PSResource -Name $testModuleName -Repository $PSGalleryName | Install-PSResource -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- }
-
- It "Install resource under specified in PSModulePath" {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- ($env:PSModulePath).Contains($pkg.InstalledLocation)
- }
-
- It "Install resource with companyname, copyright and repository source location and validate" {
- Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository PSGallery -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Version | Should -Be "5.2.5"
- $pkg.Prerelease | Should -Be "alpha001"
-
- $pkg.CompanyName | Should -Be "Anam"
- $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved."
- $pkg.RepositorySourceLocation | Should -Be $PSGalleryUri
- }
-
-
- It "Install script with companyname, copyright, and repository source location and validate" {
- Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -Repository $PSGalleryName -TrustRepository
-
- $res = Get-PSResource "Install-VSCode" -Version "1.4.2"
- $res.Name | Should -Be "Install-VSCode"
- $res.Version | Should -Be "1.4.2.0"
- $res.CompanyName | Should -Be "Microsoft Corporation"
- $res.Copyright | Should -Be "(c) Microsoft Corporation"
- $res.RepositorySourceLocation | Should -Be $PSGalleryUri
- }
-
- # Windows only
- It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
- }
-
- # Windows only
- It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) {
- Install-PSResource -Name "testmodule99" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose
- $pkg = Get-Module "testmodule99" -ListAvailable
- $pkg.Name | Should -Be "testmodule99"
- $pkg.Path.ToString().Contains("Program Files")
- }
-
- # Windows only
- It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
- }
-
- # Unix only
- # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
- It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
- }
-
- # Unix only
- # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
- It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
- }
-
- It "Should not install resource that is already installed" {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue
- $WarningVar | Should -Not -BeNullOrEmpty
- }
-
- It "Reinstall resource that is already installed with -Reinstall parameter" {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -Reinstall -TrustRepository
- $pkg = Get-PSResource $testModuleName
- $pkg.Name | Should -Be $testModuleName
- $pkg.Version | Should -Be "5.0.0.0"
- }
-
- It "Restore resource after reinstall fails" {
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
- $pkg = Get-Module $testModuleName -ListAvailable
- $pkg.Name | Should -Contain $testModuleName
- $pkg.Version | Should -Contain "5.0.0.0"
-
- $resourcePath = Split-Path -Path $pkg.Path -Parent
- $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse
-
- # Lock resource file to prevent reinstall from succeeding.
- $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
- try
- {
- # Reinstall of resource should fail with one of its files locked.
- Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent
- $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource'
- }
- finally
- {
- $fs.Close()
- }
-
- # Verify that resource module has been restored.
- (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count
- }
-
- # It "Install resource that requires accept license with -AcceptLicense flag" {
- # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense
- # $pkg = Get-PSResource "testModuleWithlicense"
- # $pkg.Name | Should -Be "testModuleWithlicense"
- # $pkg.Version | Should -Be "0.0.3.0"
- # }
-
-
- It "Install resource with cmdlet names from a module already installed (should clobber)" {
- Install-PSResource -Name "CLobberTestModule1" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource "ClobberTestModule1"
- $pkg.Name | Should -Be "ClobberTestModule1"
- $pkg.Version | Should -Be "0.0.1.0"
-
- Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository
- $pkg = Get-PSResource "ClobberTestModule2"
- $pkg.Name | Should -Be "ClobberTestModule2"
- $pkg.Version | Should -Be "0.0.1.0"
- }
-
- It "Install resource from local repository given Repository parameter" {
- $publishModuleName = "TestFindModule"
- $repoName = "psgettestlocal"
- Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleName $repoName
- Set-PSResourceRepository "psgettestlocal" -Trusted:$true
-
- Install-PSResource -Name $publishModuleName -Repository $repoName
- $pkg = Get-PSResource $publishModuleName
- $pkg | Should -Not -BeNullOrEmpty
- $pkg.Name | Should -Be $publishModuleName
- }
-
- It "Install module using -WhatIf, should not install the module" {
- Install-PSResource -Name $testModuleName -WhatIf
-
- $res = Get-PSResource $testModuleName
- $res | Should -BeNullOrEmpty
- }
-
- It "Validates that a module with module-name script files (like Pester) installs under Modules path" {
-
- Install-PSResource -Name "testModuleWithScript" -Repository $PSGalleryName -TrustRepository
-
- $res = Get-PSResource "testModuleWithScript"
- $res.InstalledLocation.ToString().Contains("Modules") | Should -Be $true
- }
-
- It "Install module using -NoClobber, should throw clobber error and not install the module" {
- Install-PSResource -Name "ClobberTestModule1" -Repository $PSGalleryName -TrustRepository
-
- $res = Get-PSResource "ClobberTestModule1"
- $res.Name | Should -Be "ClobberTestModule1"
-
- Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorAction SilentlyContinue
- $Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- It "Install PSResourceInfo object piped in" {
- Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName | Install-PSResource -TrustRepository
- $res = Get-PSResource -Name $testModuleName
- $res.Name | Should -Be $testModuleName
- $res.Version | Should -Be "1.0.0.0"
- }
-
- It "Install module using -PassThru" {
- $res = Install-PSResource -Name $testModuleName -Repository $PSGalleryName -PassThru -TrustRepository
- $res.Name | Should -Contain $testModuleName
- }
-
- It "Install modules using -RequiredResource with hashtable" {
- $rrHash = @{
- test_module = @{
- version = "[1.0.0,5.0.0)"
- repository = $PSGalleryName
- }
-
- test_module2 = @{
- version = "[1.0.0,3.0.0)"
- repository = $PSGalleryName
- prerelease = "true"
- }
-
- TestModule99 = @{}
- }
-
- Install-PSResource -RequiredResource $rrHash -TrustRepository
-
- $res1 = Get-PSResource $testModuleName
- $res1.Name | Should -Be $testModuleName
- $res1.Version | Should -Be "3.0.0.0"
-
- $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
- $res2.Name | Should -Be "test_module2"
- $res2.Version | Should -Be "2.5.0"
- $res2.Prerelease | Should -Be "beta"
-
- $res3 = Get-PSResource $testModuleName2
- $res3.Name | Should -Be $testModuleName2
- $res3.Version | Should -Be "0.0.93.0"
- }
-
- It "Install modules using -RequiredResource with JSON string" {
- $rrJSON = "{
- 'test_module': {
- 'version': '[1.0.0,5.0.0)',
- 'repository': 'PSGallery'
- },
- 'test_module2': {
- 'version': '[1.0.0,3.0.0)',
- 'repository': 'PSGallery',
- 'prerelease': 'true'
- },
- 'TestModule99': {
- 'repository': 'PSGallery'
- }
- }"
-
- Install-PSResource -RequiredResource $rrJSON -TrustRepository
-
- $res1 = Get-PSResource $testModuleName
- $res1.Name | Should -Be $testModuleName
- $res1.Version | Should -Be "3.0.0.0"
-
- $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
- $res2.Name | Should -Be "test_module2"
- $res2.Version | Should -Be "2.5.0"
- $res2.Prerelease | Should -Be "beta"
-
- $res3 = Get-PSResource $testModuleName2
- $res3.Name | Should -Be $testModuleName2
- $res3.Version | Should -Be "0.0.93.0"
- }
-
- It "Install modules using -RequiredResourceFile with PSD1 file" {
- $rrFilePSD1 = Join-Path -Path $psscriptroot -ChildPath $RequiredResourcePSD1FileName
-
- Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository
-
- $res1 = Get-PSResource $testModuleName
- $res1.Name | Should -Be $testModuleName
- $res1.Version | Should -Be "3.0.0.0"
-
- $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
- $res2.Name | Should -Be "test_module2"
- $res2.Version | Should -Be "2.5.0"
- $res2.Prerelease | Should -Be "beta"
-
- $res3 = Get-PSResource $testModuleName2
- $res3.Name | Should -Be $testModuleName2
- $res3.Version | Should -Be "0.0.93.0"
- }
-
- It "Install modules using -RequiredResourceFile with JSON file" {
- $rrFileJSON = Join-Path -Path $psscriptroot -ChildPath $RequiredResourceJSONFileName
-
- Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository
-
- $res1 = Get-PSResource $testModuleName
- $res1.Name | Should -Be $testModuleName
- $res1.Version | Should -Be "3.0.0.0"
-
- $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
- $res2.Name | Should -Be "test_module2"
- $res2.Version | Should -Be "2.5.0"
- $res2.Prerelease | Should -Be "beta"
-
- $res3 = Get-PSResource $testModuleName2
- $res3.Name | Should -Be $testModuleName2
- $res3.Version | Should -Be "0.0.93.0"
- }
-
- # Install module 1.4.3 (is authenticode signed and has catalog file)
- # Should install successfully
- It "Install modules with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
-
- $res1 = Get-PSResource $PackageManagement -Version "1.4.3"
- $res1.Name | Should -Be $PackageManagement
- $res1.Version | Should -Be "1.4.3.0"
- }
-
- # Install module 1.4.7 (is authenticode signed and has no catalog file)
- # Should not install successfully
- It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $PackageManagement -Version "1.4.7" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
-
- $res1 = Get-PSResource $PackageManagement -Version "1.4.7"
- $res1.Name | Should -Be $PackageManagement
- $res1.Version | Should -Be "1.4.7.0"
- }
-
- # Install module that is not authenticode signed
- # Should FAIL to install the module
- It "Install module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
- { Install-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- # Install 1.4.4.1 (with incorrect catalog file)
- # Should FAIL to install the module
- It "Install module with incorrect catalog file" -Skip:(!(Get-IsWindows)) {
- { Install-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- # Install script that is signed
- # Should install successfully
- It "Install script that is authenticode signed" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
-
- $res1 = Get-PSResource "Install-VSCode" -Version "1.4.2"
- $res1.Name | Should -Be "Install-VSCode"
- $res1.Version | Should -Be "1.4.2.0"
- }
-
- # Install script that is not signed
- # Should throw
- It "Install script that is not signed" -Skip:(!(Get-IsWindows)) {
- { Install-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
- }
-
- It "Install module with -NoClobber parameter" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $TestModuleName -Version "5.0.0" -Repository $PSGalleryName -NoClobber -Reinstall -TrustRepository
-
- $res = Get-PSResource $TestModuleName
- $res.Name | Should -Be $TestModuleName
- $res.Version | Should -Be "5.0.0.0"
- }
-}
-
-<# Temporarily commented until -Tag is implemented for this Describe block
-Describe 'Test Install-PSResource for interactive and root user scenarios' {
-
- BeforeAll{
- $TestGalleryName = Get-PoshTestGalleryName
- $PSGalleryName = Get-PSGalleryName
- $NuGetGalleryName = Get-NuGetGalleryName
- Get-NewPSResourceRepositoryFile
- Register-LocalRepos
- }
-
- AfterEach {
- Uninstall-PSResource "TestModule", "testModuleWithlicense" -SkipDependencyCheck -ErrorAction SilentlyContinue
- }
-
- AfterAll {
- Get-RevertPSResourceRepositoryFile
- }
-
- # Unix only manual test
- # Expected path should be similar to: '/usr/local/share/powershell/Modules'
- It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) {
- Install-PSResource -Name "TestModule" -Repository $TestGalleryName -Scope AllUsers
- $pkg = Get-Module "TestModule" -ListAvailable
- $pkg.Name | Should -Be "TestModule"
- $pkg.Path.Contains("/usr/") | Should -Be $true
- }
-
- # This needs to be manually tested due to prompt
- It "Install resource that requires accept license without -AcceptLicense flag" {
- Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName
- $pkg = Get-PSResource "testModuleWithlicense"
- $pkg.Name | Should -Be "testModuleWithlicense"
- $pkg.Version | Should -Be "0.0.1.0"
- }
-
- # This needs to be manually tested due to prompt
- It "Install resource should prompt 'trust repository' if repository is not trusted" {
- Set-PSResourceRepository PoshTestGallery -Trusted:$false
-
- Install-PSResource -Name "TestModule" -Repository $TestGalleryName -confirm:$false
-
- $pkg = Get-Module "TestModule" -ListAvailable
- $pkg.Name | Should -Be "TestModule"
-
- Set-PSResourceRepository PoshTestGallery -Trusted
- }
-}
-#>
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
+
+Describe 'Test Install-PSResource for Module' {
+
+ BeforeAll {
+ $PSGalleryName = Get-PSGalleryName
+ $PSGalleryUri = Get-PSGalleryLocation
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $testModuleName = "test_module"
+ $testModuleName2 = "TestModule99"
+ $testScriptName = "test_script"
+ $PackageManagement = "PackageManagement"
+ $RequiredResourceJSONFileName = "TestRequiredResourceFile.json"
+ $RequiredResourcePSD1FileName = "TestRequiredResourceFile.psd1"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "test_module", "test_module2", "test_script", "TestModule99", "testModuleWithlicense", "TestFindModule","ClobberTestModule1", "ClobberTestModule2", "PackageManagement" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ $testCases = @{Name="*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test_Module*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test?Module","Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}
+
+ It "Should not install resource with wildcard in name" -TestCases $testCases {
+ param($Name, $ErrorId)
+ Install-PSResource -Name $Name -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ It "Install specific module resource by name" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install specific script resource by name" {
+ Install-PSResource -Name $testScriptName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testScriptName
+ $pkg.Name | Should -Be $testScriptName
+ $pkg.Version | Should -Be "3.5.0.0"
+ }
+
+ It "Install multiple resources by name" {
+ $pkgNames = @($testModuleName,$testModuleName2)
+ Install-PSResource -Name $pkgNames -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $pkgNames
+ $pkg.Name | Should -Be $pkgNames
+ }
+
+ It "Should not install resource given nonexistant name" {
+ Install-PSResource -Name "NonExistantModule" -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $pkg = Get-PSResource "NonExistantModule"
+ $pkg.Name | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "ResourceNotFoundError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # Do some version testing, but Find-PSResource should be doing thorough testing
+ It "Should install resource given name and exact version" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Should install resource given name and exact version with bracket syntax" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "3.0.0.0"
+ }
+
+ # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw
+ It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version = "(1.0.0.0)"
+ try {
+ Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue
+ }
+ catch
+ {}
+ $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Should not install resource with incorrectly formatted version such as version formatted with invalid delimiter [1-0-0-0]" {
+ $Version="[1-0-0-0]"
+ try {
+ Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue
+ }
+ catch
+ {}
+ $Error[0].FullyQualifiedErrorId | Should -be "ResourceNotFoundError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Install resource when given Name, Version '*', should install the latest version" {
+ Install-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install resource with latest (including prerelease) version given Prerelease parameter" {
+ Install-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+ }
+
+ It "Install a module with a dependency" {
+ Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
+ Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
+
+ $pkg = Get-PSResource "TestModuleWithDependencyC"
+ $pkg.Name | Should -Be "TestModuleWithDependencyC"
+ $pkg.Version | Should -Be "1.0.0.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyB"
+ $pkg.Name | Should -Be "TestModuleWithDependencyB"
+ $pkg.Version | Should -Be "3.0.0.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyD"
+ $pkg.Name | Should -Be "TestModuleWithDependencyD"
+ $pkg.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Install a module with a dependency and skip installing the dependency" {
+ Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
+ Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "TestModuleWithDependencyC"
+ $pkg.Name | Should -Be "TestModuleWithDependencyC"
+ $pkg.Version | Should -Be "1.0.0.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyB", "TestModuleWithDependencyD"
+ $pkg | Should -BeNullOrEmpty
+ }
+
+ It "Install resource via InputObject by piping from Find-PSresource" {
+ Find-PSResource -Name $testModuleName -Repository $PSGalleryName | Install-PSResource -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install resource under specified in PSModulePath" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ ($env:PSModulePath).Contains($pkg.InstalledLocation)
+ }
+
+ It "Install resource with companyname, copyright and repository source location and validate" {
+ Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository PSGallery -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+
+ $pkg.CompanyName | Should -Be "Anam"
+ $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved."
+ $pkg.RepositorySourceLocation | Should -Be $PSGalleryUri
+ }
+
+
+ It "Install script with companyname, copyright, and repository source location and validate" {
+ Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource "Install-VSCode" -Version "1.4.2"
+ $res.Name | Should -Be "Install-VSCode"
+ $res.Version | Should -Be "1.4.2.0"
+ $res.CompanyName | Should -Be "Microsoft Corporation"
+ $res.Copyright | Should -Be "(c) Microsoft Corporation"
+ $res.RepositorySourceLocation | Should -Be $PSGalleryUri
+ }
+
+ # Windows only
+ It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Windows only
+ It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) {
+ Install-PSResource -Name "testmodule99" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose
+ $pkg = Get-Module "testmodule99" -ListAvailable
+ $pkg.Name | Should -Be "testmodule99"
+ $pkg.Path.ToString().Contains("Program Files")
+ }
+
+ # Windows only
+ It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ It "Should not install resource that is already installed" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue
+ $WarningVar | Should -Not -BeNullOrEmpty
+ }
+
+ It "Reinstall resource that is already installed with -Reinstall parameter" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -Reinstall -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Restore resource after reinstall fails" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-Module $testModuleName -ListAvailable
+ $pkg.Name | Should -Contain $testModuleName
+ $pkg.Version | Should -Contain "5.0.0.0"
+
+ $resourcePath = Split-Path -Path $pkg.Path -Parent
+ $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse
+
+ # Lock resource file to prevent reinstall from succeeding.
+ $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
+ try
+ {
+ # Reinstall of resource should fail with one of its files locked.
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent
+ $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource'
+ }
+ finally
+ {
+ $fs.Close()
+ }
+
+ # Verify that resource module has been restored.
+ (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count
+ }
+
+ # It "Install resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense
+ # $pkg = Get-PSResource "testModuleWithlicense"
+ # $pkg.Name | Should -Be "testModuleWithlicense"
+ # $pkg.Version | Should -Be "0.0.3.0"
+ # }
+
+
+ It "Install resource with cmdlet names from a module already installed (should clobber)" {
+ Install-PSResource -Name "CLobberTestModule1" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "ClobberTestModule1"
+ $pkg.Name | Should -Be "ClobberTestModule1"
+ $pkg.Version | Should -Be "0.0.1.0"
+
+ Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "ClobberTestModule2"
+ $pkg.Name | Should -Be "ClobberTestModule2"
+ $pkg.Version | Should -Be "0.0.1.0"
+ }
+
+ It "Install resource from local repository given Repository parameter" {
+ $publishModuleName = "TestFindModule"
+ $repoName = "psgettestlocal"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleName $repoName
+ Set-PSResourceRepository "psgettestlocal" -Trusted:$true
+
+ Install-PSResource -Name $publishModuleName -Repository $repoName
+ $pkg = Get-PSResource $publishModuleName
+ $pkg | Should -Not -BeNullOrEmpty
+ $pkg.Name | Should -Be $publishModuleName
+ }
+
+ It "Install module using -WhatIf, should not install the module" {
+ Install-PSResource -Name $testModuleName -WhatIf
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Validates that a module with module-name script files (like Pester) installs under Modules path" {
+
+ Install-PSResource -Name "testModuleWithScript" -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource "testModuleWithScript"
+ $res.InstalledLocation.ToString().Contains("Modules") | Should -Be $true
+ }
+
+ It "Install module using -NoClobber, should throw clobber error and not install the module" {
+ Install-PSResource -Name "ClobberTestModule1" -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource "ClobberTestModule1"
+ $res.Name | Should -Be "ClobberTestModule1"
+
+ Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorAction SilentlyContinue
+ $Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ It "Install PSResourceInfo object piped in" {
+ Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName | Install-PSResource -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Install module using -PassThru" {
+ $res = Install-PSResource -Name $testModuleName -Repository $PSGalleryName -PassThru -TrustRepository
+ $res.Name | Should -Contain $testModuleName
+ }
+
+ It "Install modules using -RequiredResource with hashtable" {
+ $rrHash = @{
+ test_module = @{
+ version = "[1.0.0,5.0.0)"
+ repository = $PSGalleryName
+ }
+
+ test_module2 = @{
+ version = "[1.0.0,3.0.0)"
+ repository = $PSGalleryName
+ prerelease = "true"
+ }
+
+ TestModule99 = @{}
+ }
+
+ Install-PSResource -RequiredResource $rrHash -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93.0"
+ }
+
+ It "Install modules using -RequiredResource with JSON string" {
+ $rrJSON = "{
+ 'test_module': {
+ 'version': '[1.0.0,5.0.0)',
+ 'repository': 'PSGallery'
+ },
+ 'test_module2': {
+ 'version': '[1.0.0,3.0.0)',
+ 'repository': 'PSGallery',
+ 'prerelease': 'true'
+ },
+ 'TestModule99': {
+ 'repository': 'PSGallery'
+ }
+ }"
+
+ Install-PSResource -RequiredResource $rrJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93.0"
+ }
+
+ It "Install modules using -RequiredResourceFile with PSD1 file" {
+ $rrFilePSD1 = Join-Path -Path $psscriptroot -ChildPath $RequiredResourcePSD1FileName
+
+ Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93.0"
+ }
+
+ It "Install modules using -RequiredResourceFile with JSON file" {
+ $rrFileJSON = Join-Path -Path $psscriptroot -ChildPath $RequiredResourceJSONFileName
+
+ Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93.0"
+ }
+
+ # Install module 1.4.3 (is authenticode signed and has catalog file)
+ # Should install successfully
+ It "Install modules with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource $PackageManagement -Version "1.4.3"
+ $res1.Name | Should -Be $PackageManagement
+ $res1.Version | Should -Be "1.4.3.0"
+ }
+
+ # Install module 1.4.7 (is authenticode signed and has no catalog file)
+ # Should not install successfully
+ It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $PackageManagement -Version "1.4.7" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource $PackageManagement -Version "1.4.7"
+ $res1.Name | Should -Be $PackageManagement
+ $res1.Version | Should -Be "1.4.7.0"
+ }
+
+ # Install module that is not authenticode signed
+ # Should FAIL to install the module
+ It "Install module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ { Install-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # Install 1.4.4.1 (with incorrect catalog file)
+ # Should FAIL to install the module
+ It "Install module with incorrect catalog file" -Skip:(!(Get-IsWindows)) {
+ { Install-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # Install script that is signed
+ # Should install successfully
+ It "Install script that is authenticode signed" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource "Install-VSCode" -Version "1.4.2"
+ $res1.Name | Should -Be "Install-VSCode"
+ $res1.Version | Should -Be "1.4.2.0"
+ }
+
+ # Install script that is not signed
+ # Should throw
+ It "Install script that is not signed" -Skip:(!(Get-IsWindows)) {
+ { Install-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+}
+
+<# Temporarily commented until -Tag is implemented for this Describe block
+Describe 'Test Install-PSResource for interactive and root user scenarios' {
+
+ BeforeAll{
+ $TestGalleryName = Get-PoshTestGalleryName
+ $PSGalleryName = Get-PSGalleryName
+ $NuGetGalleryName = Get-NuGetGalleryName
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "TestModule", "testModuleWithlicense" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ # Unix only manual test
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -Scope AllUsers
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+ $pkg.Path.Contains("/usr/") | Should -Be $true
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource that requires accept license without -AcceptLicense flag" {
+ Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName
+ $pkg = Get-PSResource "testModuleWithlicense"
+ $pkg.Name | Should -Be "testModuleWithlicense"
+ $pkg.Version | Should -Be "0.0.1.0"
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource should prompt 'trust repository' if repository is not trusted" {
+ Set-PSResourceRepository PoshTestGallery -Trusted:$false
+
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -confirm:$false
+
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+
+ Set-PSResourceRepository PoshTestGallery -Trusted
+ }
+}
+#>
diff --git a/test/InstallPSResourceTests/InstallPSResourceLocal.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceLocal.Tests.ps1
new file mode 100644
index 000000000..cda98947c
--- /dev/null
+++ b/test/InstallPSResourceTests/InstallPSResourceLocal.Tests.ps1
@@ -0,0 +1,275 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test Install-PSResource for local repositories' {
+
+
+ BeforeAll {
+ $localRepo = "psgettestlocal"
+ $moduleName = "test_local_mod"
+ $moduleName2 = "test_local_mod2"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "3.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "5.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName2 $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName2 $localRepo "5.0.0"
+ }
+
+ AfterEach {
+ Uninstall-PSResource $moduleName, $moduleName2 -Version "*"
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Update resource installed given Name parameter" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ It "Update resources installed given Name (with wildcard) parameter" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+ Install-PSResource -Name $moduleName2 -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name "test_local*" -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name "test_local*" -Version "5.0.0"
+
+ $inputHashtable = @{test_module = "1.0.0"; test_module2 = "1.0.0"}
+ $isTest_ModuleUpdated = $false
+ $isTest_Module2Updated = $false
+ foreach ($item in $res)
+ {
+ if ([System.Version]$item.Version -gt [System.Version]$inputHashtable[$item.Name])
+ {
+ if ($item.Name -like $moduleName)
+ {
+ $isTest_ModuleUpdated = $true
+ }
+ elseif ($item.Name -like $moduleName2)
+ {
+ $isTest_Module2Updated = $true
+ }
+ }
+ }
+
+ $isTest_ModuleUpdated | Should -BeTrue
+ $isTest_Module2Updated | Should -BeTrue
+ }
+
+ It "Update resource installed given Name and Version (specific) parameters" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name $moduleName -Version "5.0.0" -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name $moduleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -eq [System.Version]"5.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -BeTrue
+ }
+
+ # Windows only
+ It "update resource under CurrentUser scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ # TODO: perhaps also install TestModule with the highest version (the one above 1.2.0.0) to the AllUsers path too
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Version "3.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under AllUsers scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ $res = Get-Module -Name $moduleName -ListAvailable
+ $res | Should -Not -BeNullOrEmpty
+ $res.Version | Should -Contain "5.0.0"
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.ModuleBase.Contains("Program") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+ $isPkgUpdated | Should -Be $true
+
+ }
+
+ # Windows only
+ It "Update resource under no specified scope" -skip:(!$IsWindows) {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+ Update-PSResource -Name $moduleName -Version "5.0.0.0" -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ # this line is commented out because AllUsers scope requires sudo and that isn't supported in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ # this test is skipped because it requires sudo to run and has yet to be resolved in CI
+ It "Update resource under AllUsers scope - Unix only" -Skip:($true) {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ Update-PSResource -Name $moduleName -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("usr") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ # this is commented out because it requires sudo to run with AllUsers scope and this hasn't been resolved in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # It "update resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "TestModuleWithLicense" -Version "0.0.1.0" -Repository $TestGalleryName -AcceptLicense
+ # Update-PSResource -Name "TestModuleWithLicense" -Repository $TestGalleryName -AcceptLicense
+ # $res = Get-PSResource "TestModuleWithLicense"
+
+ # $isPkgUpdated = $false
+ # foreach ($pkg in $res)
+ # {
+ # if ([System.Version]$pkg.Version -gt [System.Version]"0.0.1.0")
+ # {
+ # $isPkgUpdated = $true
+ # }
+ # }
+
+ # $isPkgUpdated | Should -Be $true
+ # }
+
+ It "Update module using -WhatIf, should not update the module" {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+ Update-PSResource -Name $moduleName -WhatIf -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource installed given -Name and -PassThru parameters" {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+
+ $res = Update-PSResource -Name $moduleName -Version "5.0.0.0" -Repository $localRepo -TrustRepository -PassThru
+ $res.Name | Should -Contain $moduleName
+ $res.Version | Should -Be "5.0.0.0"
+ }
+}
diff --git a/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1
new file mode 100644
index 000000000..9a83695eb
--- /dev/null
+++ b/test/InstallPSResourceTests/InstallPSResourceV2Server.Tests.ps1
@@ -0,0 +1,543 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test Install-PSResource for V2 Server scenarios' {
+
+ BeforeAll {
+ $PSGalleryName = Get-PSGalleryName
+ $PSGalleryUri = Get-PSGalleryLocation
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $testModuleName = "test_module"
+ $testModuleName2 = "TestModule99"
+ $testScriptName = "test_script"
+ $PackageManagement = "PackageManagement"
+ $RequiredResourceJSONFileName = "TestRequiredResourceFile.json"
+ $RequiredResourcePSD1FileName = "TestRequiredResourceFile.psd1"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "test_module", "test_module2", "test_script", "TestModule99", "testModuleWithlicense", "TestFindModule","ClobberTestModule1", "ClobberTestModule2", "PackageManagement" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ $testCases = @{Name="*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test_Module*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test?Module","Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}
+
+ It "Should not install resource with wildcard in name" -TestCases $testCases {
+ param($Name, $ErrorId)
+ Install-PSResource -Name $Name -Repository $PSGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ It "Install specific module resource by name" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install specific script resource by name" {
+ Install-PSResource -Name $testScriptName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testScriptName
+ $pkg.Name | Should -Be $testScriptName
+ $pkg.Version | Should -Be "3.5.0.0"
+ }
+
+ It "Install multiple resources by name" {
+ $pkgNames = @($testModuleName, $testModuleName2)
+ Install-PSResource -Name $pkgNames -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $pkgNames
+ $pkg.Name | Should -Be $pkgNames
+ }
+
+ It "Should not install resource given nonexistant name" {
+ Install-PSResource -Name "NonExistantModule" -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $pkg = Get-PSResource "NonExistantModule"
+ $pkg.Name | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # Do some version testing, but Find-PSResource should be doing thorough testing
+ It "Should install resource given name and exact version" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Should install resource given name and exact version with bracket syntax" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "3.0.0.0"
+ }
+
+ # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw
+ It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version = "(1.0.0.0)"
+ try {
+ Install-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -TrustRepository -ErrorAction SilentlyContinue
+ }
+ catch
+ {}
+ $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Install resource when given Name, Version '*', should install the latest version" {
+ Install-PSResource -Name $testModuleName -Version "*" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install resource with latest (including prerelease) version given Prerelease parameter" {
+ Install-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+ }
+
+ It "Install a module with a dependency" {
+ Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
+ Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
+
+ $pkg = Get-PSResource "TestModuleWithDependencyC"
+ $pkg.Name | Should -Be "TestModuleWithDependencyC"
+ $pkg.Version | Should -Be "1.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyB"
+ $pkg.Name | Should -Be "TestModuleWithDependencyB"
+ $pkg.Version | Should -Be "3.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyD"
+ $pkg.Name | Should -Be "TestModuleWithDependencyD"
+ $pkg.Version | Should -Be "1.0"
+ }
+
+ It "Install a module with a dependency and skip installing the dependency" {
+ Uninstall-PSResource -Name "TestModuleWithDependency*" -Version "*" -SkipDependencyCheck
+ Install-PSResource -Name "TestModuleWithDependencyC" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "TestModuleWithDependencyC"
+ $pkg.Name | Should -Be "TestModuleWithDependencyC"
+ $pkg.Version | Should -Be "1.0"
+
+ $pkg = Get-PSResource "TestModuleWithDependencyB", "TestModuleWithDependencyD"
+ $pkg | Should -BeNullOrEmpty
+ }
+
+ It "Install resource via InputObject by piping from Find-PSresource" {
+ Find-PSResource -Name $testModuleName -Repository $PSGalleryName | Install-PSResource -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ It "Install resource under specified in PSModulePath" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ ($env:PSModulePath).Contains($pkg.InstalledLocation)
+ }
+
+ It "Install resource with companyname, copyright and repository source location and validate" {
+ Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository PSGallery -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+
+ $pkg.CompanyName | Should -Be "Anam"
+ $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved."
+ $pkg.RepositorySourceLocation | Should -Be $PSGalleryUri
+ }
+
+
+ It "Install script with companyname, copyright, and repository source location and validate" {
+ Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource "Install-VSCode" -Version "1.4.2"
+ $res.Name | Should -Be "Install-VSCode"
+ $res.Version | Should -Be "1.4.2"
+ $res.CompanyName | Should -Be "Microsoft Corporation"
+ $res.Copyright | Should -Be "(c) Microsoft Corporation"
+ $res.RepositorySourceLocation | Should -Be $PSGalleryUri
+ }
+
+ # Windows only
+ It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Windows only
+ It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) {
+ Install-PSResource -Name "testmodule99" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose
+ $pkg = Get-Module "testmodule99" -ListAvailable
+ $pkg.Name | Should -Be "testmodule99"
+ $pkg.Path.ToString().Contains("Program Files")
+ }
+
+ # Windows only
+ It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ It "Should not install resource that is already installed" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue
+ $WarningVar | Should -Not -BeNullOrEmpty
+ }
+
+ It "Reinstall resource that is already installed with -Reinstall parameter" {
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ Install-PSResource -Name $testModuleName -Repository $PSGalleryName -Reinstall -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0.0"
+ }
+
+ # It "Restore resource after reinstall fails" {
+ # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+ # $pkg = Get-PSResource $testModuleName
+ # $pkg.Name | Should -Contain $testModuleName
+ # $pkg.Version | Should -Contain "5.0.0.0"
+
+ # $resourcePath = Split-Path -Path $pkg.InstalledLocation -Parent
+ # $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse
+
+ # # Lock resource file to prevent reinstall from succeeding.
+ # $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
+ # try
+ # {
+ # # Reinstall of resource should fail with one of its files locked.
+ # Install-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent
+ # $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource'
+ # }
+ # finally
+ # {
+ # $fs.Close()
+ # }
+
+ # # Verify that resource module has been restored.
+ # (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count
+ # }
+
+ # It "Install resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense
+ # $pkg = Get-PSResource "testModuleWithlicense"
+ # $pkg.Name | Should -Be "testModuleWithlicense"
+ # $pkg.Version | Should -Be "0.0.3.0"
+ # }
+
+
+ It "Install resource with cmdlet names from a module already installed (should clobber)" {
+ Install-PSResource -Name "CLobberTestModule1" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "ClobberTestModule1"
+ $pkg.Name | Should -Be "ClobberTestModule1"
+ $pkg.Version | Should -Be "0.0.1"
+
+ Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository
+ $pkg = Get-PSResource "ClobberTestModule2"
+ $pkg.Name | Should -Be "ClobberTestModule2"
+ $pkg.Version | Should -Be "0.0.1"
+ }
+
+ It "Install module using -WhatIf, should not install the module" {
+ Install-PSResource -Name $testModuleName -WhatIf
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Validates that a module with module-name script files (like Pester) installs under Modules path" {
+
+ Install-PSResource -Name "testModuleWithScript" -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource "testModuleWithScript"
+ $res.InstalledLocation.ToString().Contains("Modules") | Should -Be $true
+ }
+
+ # It "Install module using -NoClobber, should throw clobber error and not install the module" {
+ # Install-PSResource -Name "ClobberTestModule1" -Repository $PSGalleryName -TrustRepository
+
+ # $res = Get-PSResource "ClobberTestModule1"
+ # $res.Name | Should -Be "ClobberTestModule1"
+
+ # Install-PSResource -Name "ClobberTestModule2" -Repository $PSGalleryName -TrustRepository -NoClobber -ErrorAction SilentlyContinue
+ # $Error[0].FullyQualifiedErrorId | Should -be "CommandAlreadyExists,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ # }
+
+ It "Install PSResourceInfo object piped in" {
+ Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName | Install-PSResource -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0.0"
+ }
+
+ It "Install module using -PassThru" {
+ $res = Install-PSResource -Name $testModuleName -Repository $PSGalleryName -PassThru -TrustRepository
+ $res.Name | Should -Contain $testModuleName
+ }
+
+ It "Install modules using -RequiredResource with hashtable" {
+ $rrHash = @{
+ test_module = @{
+ version = "[1.0.0,5.0.0)"
+ repository = $PSGalleryName
+ }
+
+ test_module2 = @{
+ version = "[1.0.0,3.0.0)"
+ repository = $PSGalleryName
+ prerelease = "true"
+ }
+
+ TestModule99 = @{
+ repository = $PSGalleryName
+ }
+ }
+
+ Install-PSResource -RequiredResource $rrHash -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResource with JSON string" {
+ $rrJSON = "{
+ 'test_module': {
+ 'version': '[1.0.0,5.0.0)',
+ 'repository': 'PSGallery'
+ },
+ 'test_module2': {
+ 'version': '[1.0.0,3.0.0)',
+ 'repository': 'PSGallery',
+ 'prerelease': 'true'
+ },
+ 'TestModule99': {
+ 'repository': 'PSGallery'
+ }
+ }"
+
+ Install-PSResource -RequiredResource $rrJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResourceFile with PSD1 file" {
+ $rrFilePSD1 = Join-Path -Path "$((Get-Item $psscriptroot).parent)" -ChildPath $RequiredResourcePSD1FileName
+
+ Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResourceFile with JSON file" {
+ $rrFileJSON = Join-Path -Path "$((Get-Item $psscriptroot).parent)" -ChildPath $RequiredResourceJSONFileName
+
+ Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource "test_module2" -Version "2.5.0-beta"
+ $res2.Name | Should -Be "test_module2"
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource $testModuleName2
+ $res3.Name | Should -Be $testModuleName2
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ # Install module 1.4.3 (is authenticode signed and has catalog file)
+ # Should install successfully
+ It "Install modules with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource $PackageManagement -Version "1.4.3"
+ $res1.Name | Should -Be $PackageManagement
+ $res1.Version | Should -Be "1.4.3"
+ }
+
+ # Install module 1.4.7 (is authenticode signed and has no catalog file)
+ # Should not install successfully
+ It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $PackageManagement -Version "1.4.7" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource $PackageManagement -Version "1.4.7"
+ $res1.Name | Should -Be $PackageManagement
+ $res1.Version | Should -Be "1.4.7"
+ }
+
+ # Install module that is not authenticode signed
+ # Should FAIL to install the module
+ It "Install module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # # Install 1.4.4.1 (with incorrect catalog file)
+ # # Should FAIL to install the module
+ # It "Install module with incorrect catalog file" -Skip:(!(Get-IsWindows)) {
+ # { Install-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ # }
+
+ # Install script that is signed
+ # Should install successfully
+ It "Install script that is authenticode signed" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name "Install-VSCode" -Version "1.4.2" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+
+ $res1 = Get-PSResource "Install-VSCode" -Version "1.4.2"
+ $res1.Name | Should -Be "Install-VSCode"
+ $res1.Version | Should -Be "1.4.2"
+ }
+
+ # Install script that is not signed
+ # Should throw
+ It "Install script that is not signed" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+}
+
+<# Temporarily commented until -Tag is implemented for this Describe block
+Describe 'Test Install-PSResource for interactive and root user scenarios' {
+
+ BeforeAll{
+ $TestGalleryName = Get-PoshTestGalleryName
+ $PSGalleryName = Get-PSGalleryName
+ $NuGetGalleryName = Get-NuGetGalleryName
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "TestModule", "testModuleWithlicense" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ # Unix only manual test
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -Scope AllUsers
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+ $pkg.Path.Contains("/usr/") | Should -Be $true
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource that requires accept license without -AcceptLicense flag" {
+ Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName
+ $pkg = Get-PSResource "testModuleWithlicense"
+ $pkg.Name | Should -Be "testModuleWithlicense"
+ $pkg.Version | Should -Be "0.0.1.0"
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource should prompt 'trust repository' if repository is not trusted" {
+ Set-PSResourceRepository PoshTestGallery -Trusted:$false
+
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -confirm:$false
+
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+
+ Set-PSResourceRepository PoshTestGallery -Trusted
+ }
+}
+#>
diff --git a/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1
new file mode 100644
index 000000000..2bd9b3b8d
--- /dev/null
+++ b/test/InstallPSResourceTests/InstallPSResourceV3Server.Tests.ps1
@@ -0,0 +1,410 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test Install-PSResource for V3Server scenarios' {
+
+ BeforeAll {
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $NuGetGalleryUri = Get-NuGetGalleryLocation
+ $testModuleName = "test_module"
+ $testModuleName2 = "test_module2"
+ # $testModuleName2 = "TestModule99"
+ $testScriptName = "test_script"
+ $PackageManagement = "PackageManagement"
+ $RequiredResourceJSONFileName = "TestRequiredResourceFile.json"
+ $RequiredResourcePSD1FileName = "TestRequiredResourceFile.psd1"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "test_module", "test_module2", "test_script", "TestModule99", "testModuleWithlicense", "TestFindModule", "PackageManagement" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ $testCases = @{Name="*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test_Module*"; ErrorId="NameContainsWildcard"},
+ @{Name="Test?Module","Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}
+
+ It "Should not install resource with wildcard in name" -TestCases $testCases {
+ param($Name, $ErrorId)
+ Install-PSResource -Name $Name -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ It "Install specific module resource by name" {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ }
+
+ It "Install specific script resource by name" {
+ Install-PSResource -Name $testScriptName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testScriptName
+ $pkg.Name | Should -Be $testScriptName
+ $pkg.Version | Should -Be "3.5.0"
+ }
+
+ It "Install multiple resources by name" {
+ $pkgNames = @($testModuleName, $testModuleName2)
+ Install-PSResource -Name $pkgNames -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $pkgNames
+ $pkg.Name | Should -Be $pkgNames
+ }
+
+ It "Should not install resource given nonexistant name" {
+ Install-PSResource -Name "NonExistantModule" -Repository $NuGetGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $pkg = Get-PSResource "NonExistantModule"
+ $pkg.Name | Should -BeNullOrEmpty
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+ }
+
+ # Do some version testing, but Find-PSResource should be doing thorough testing
+ It "Should install resource given name and exact version" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0"
+ }
+
+ It "Should install resource given name and exact version with bracket syntax" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "1.0.0"
+ }
+
+ It "Should install resource given name and exact range inclusive [1.0.0, 5.0.0]" {
+ Install-PSResource -Name $testModuleName -Version "[1.0.0, 5.0.0]" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ }
+
+ It "Should install resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Install-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "3.0.0"
+ }
+
+ # TODO: Update this test and others like it that use try/catch blocks instead of Should -Throw
+ It "Should not install resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version = "(1.0.0.0)"
+ try {
+ Install-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -TrustRepository -ErrorAction SilentlyContinue
+ }
+ catch
+ {}
+ $Error[0].FullyQualifiedErrorId | Should -be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource"
+
+ $res = Get-PSResource $testModuleName
+ $res | Should -BeNullOrEmpty
+ }
+
+ It "Install resource when given Name, Version '*', should install the latest version" {
+ Install-PSResource -Name $testModuleName -Version "*" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ }
+
+ It "Install resource with latest (including prerelease) version given Prerelease parameter" {
+ Install-PSResource -Name $testModuleName -Prerelease -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+ }
+
+ It "Install resource via InputObject by piping from Find-PSresource" {
+ Find-PSResource -Name $testModuleName -Repository $NuGetGalleryName | Install-PSResource -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ }
+
+ It "Install resource under specified in PSModulePath" {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ ($env:PSModulePath).Contains($pkg.InstalledLocation)
+ }
+
+ It "Install resource with companyname, copyright and repository source location and validate properties" {
+ Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Version | Should -Be "5.2.5"
+ $pkg.Prerelease | Should -Be "alpha001"
+
+ $pkg.CompanyName | Should -Be "Anam Navied"
+ $pkg.Copyright | Should -Be "(c) Anam Navied. All rights reserved."
+ $pkg.RepositorySourceLocation | Should -Be $NuGetGalleryUri
+ }
+
+ # Windows only
+ It "Install resource under CurrentUser scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Windows only
+ It "Install resource under AllUsers scope - Windows only" -Skip:(!((Get-IsWindows) -and (Test-IsAdmin))) {
+ Install-PSResource -Name "testmodule99" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers -Verbose
+ $pkg = Get-Module "testmodule99" -ListAvailable
+ $pkg.Name | Should -Be "testmodule99"
+ $pkg.Path.ToString().Contains("Program Files")
+ }
+
+ # Windows only
+ It "Install resource under no specified scope - Windows only" -Skip:(!(Get-IsWindows)) {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("Documents") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Install resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.InstalledLocation.ToString().Contains("$env:HOME/.local") | Should -Be $true
+ }
+
+ It "Should not install resource that is already installed" {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue
+ $WarningVar | Should -Not -BeNullOrEmpty
+ }
+
+ It "Reinstall resource that is already installed with -Reinstall parameter" {
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -Reinstall -TrustRepository
+ $pkg = Get-PSResource $testModuleName
+ $pkg.Name | Should -Be $testModuleName
+ $pkg.Version | Should -Be "5.0.0"
+ }
+
+ # It "Restore resource after reinstall fails" {
+ # Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ # $pkg = Get-PSResource $testModuleName
+ # $pkg.Name | Should -Contain $testModuleName
+ # $pkg.Version | Should -Contain "5.0.0"
+
+ # $resourcePath = Split-Path -Path $pkg.InstalledLocation -Parent
+ # $resourceFiles = Get-ChildItem -Path $resourcePath -Recurse
+
+ # # Lock resource file to prevent reinstall from succeeding.
+ # $fs = [System.IO.File]::Open($resourceFiles[0].FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
+ # try
+ # {
+ # # Reinstall of resource should fail with one of its files locked.
+ # Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository -Reinstall -ErrorVariable ev -ErrorAction Silent
+ # $ev.FullyQualifiedErrorId | Should -BeExactly 'InstallPackageFailed,Microsoft.PowerShell.PowerShellGet.Cmdlets.InstallPSResource'
+ # }
+ # finally
+ # {
+ # $fs.Close()
+ # }
+
+ # # Verify that resource module has been restored.
+ # (Get-ChildItem -Path $resourcePath -Recurse).Count | Should -BeExactly $resourceFiles.Count
+ # }
+
+ # It "Install resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName -AcceptLicense
+ # $pkg = Get-PSResource "testModuleWithlicense"
+ # $pkg.Name | Should -Be "testModuleWithlicense"
+ # $pkg.Version | Should -Be "0.0.3.0"
+ # }
+
+ It "Install PSResourceInfo object piped in" {
+ Find-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName | Install-PSResource -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0"
+ }
+
+ It "Install module using -PassThru" {
+ $res = Install-PSResource -Name $testModuleName -Repository $NuGetGalleryName -PassThru -TrustRepository
+ $res.Name | Should -Contain $testModuleName
+ }
+
+ It "Install modules using -RequiredResource with hashtable" {
+ $rrHash = @{
+ test_module = @{
+ version = "[1.0.0,5.0.0)"
+ repository = $NuGetGalleryName
+ }
+
+ test_module2 = @{
+ version = "[1.0.0,5.0.0]"
+ repository = $NuGetGalleryName
+ prerelease = "true"
+ }
+
+ TestModule99 = @{
+ repository = $NuGetGalleryName
+ }
+ }
+
+ Install-PSResource -RequiredResource $rrHash -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0"
+
+ $res2 = Get-PSResource $testModuleName2
+ $res2.Name | Should -Be $testModuleName2
+ $res2.Version | Should -Be "5.0.0"
+
+ $res3 = Get-PSResource "TestModule99"
+ $res3.Name | Should -Be "TestModule99"
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResource with JSON string" {
+ $rrJSON = "{
+ 'test_module': {
+ 'version': '[1.0.0,5.0.0)',
+ 'repository': 'NuGetGallery'
+ },
+ 'test_module2': {
+ 'version': '[1.0.0,5.0.0]',
+ 'repository': 'PSGallery',
+ 'prerelease': 'true'
+ },
+ 'TestModule99': {
+ 'repository': 'NuGetGallery'
+ }
+ }"
+
+ Install-PSResource -RequiredResource $rrJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0"
+
+ $res2 = Get-PSResource $testModuleName2
+ $res2.Name | Should -Be $testModuleName2
+ $res2.Version | Should -Be "5.0.0.0"
+
+ $res3 = Get-PSResource "testModule99"
+ $res3.Name | Should -Be "testModule99"
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResourceFile with PSD1 file" {
+ $rrFilePSD1 = Join-Path -Path "$((Get-Item $psscriptroot).parent)" -ChildPath $RequiredResourcePSD1FileName
+
+ Install-PSResource -RequiredResourceFile $rrFilePSD1 -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource $testModuleName2 -Version "2.5.0-beta"
+ $res2.Name | Should -Be $testModuleName2
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource "testModule99"
+ $res3.Name | Should -Be "testModule99"
+ $res3.Version | Should -Be "0.0.93"
+ }
+
+ It "Install modules using -RequiredResourceFile with JSON file" {
+ $rrFileJSON = Join-Path -Path "$((Get-Item $psscriptroot).parent)" -ChildPath $RequiredResourceJSONFileName
+
+ Install-PSResource -RequiredResourceFile $rrFileJSON -TrustRepository
+
+ $res1 = Get-PSResource $testModuleName
+ $res1.Name | Should -Be $testModuleName
+ $res1.Version | Should -Be "3.0.0.0"
+
+ $res2 = Get-PSResource $testModuleName2 -Version "2.5.0-beta"
+ $res2.Name | Should -Be $testModuleName2
+ $res2.Version | Should -Be "2.5.0"
+ $res2.Prerelease | Should -Be "beta"
+
+ $res3 = Get-PSResource "testModule99"
+ $res3.Name | Should -Be "testModule99"
+ $res3.Version | Should -Be "0.0.93"
+ }
+}
+<# Temporarily commented until -Tag is implemented for this Describe block
+Describe 'Test Install-PSResource for interactive and root user scenarios' {
+
+ BeforeAll{
+ $TestGalleryName = Get-PoshTestGalleryName
+ $NuGetGalleryName = Get-PSGalleryName
+ $NuGetGalleryName = Get-NuGetGalleryName
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+ }
+
+ AfterEach {
+ Uninstall-PSResource "TestModule", "testModuleWithlicense" -SkipDependencyCheck -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ # Unix only manual test
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ It "Install resource under AllUsers scope - Unix only" -Skip:(Get-IsWindows) {
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -Scope AllUsers
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+ $pkg.Path.Contains("/usr/") | Should -Be $true
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource that requires accept license without -AcceptLicense flag" {
+ Install-PSResource -Name "testModuleWithlicense" -Repository $TestGalleryName
+ $pkg = Get-PSResource "testModuleWithlicense"
+ $pkg.Name | Should -Be "testModuleWithlicense"
+ $pkg.Version | Should -Be "0.0.1.0"
+ }
+
+ # This needs to be manually tested due to prompt
+ It "Install resource should prompt 'trust repository' if repository is not trusted" {
+ Set-PSResourceRepository PoshTestGallery -Trusted:$false
+
+ Install-PSResource -Name "TestModule" -Repository $TestGalleryName -confirm:$false
+
+ $pkg = Get-Module "TestModule" -ListAvailable
+ $pkg.Name | Should -Be "TestModule"
+
+ Set-PSResourceRepository PoshTestGallery -Trusted
+ }
+}
+#>
diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1
index e72984f60..759aa433a 100644
--- a/test/PSGetTestUtils.psm1
+++ b/test/PSGetTestUtils.psm1
@@ -19,10 +19,8 @@ $script:IsCoreCLR = $PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTabl
$script:PSGalleryName = 'PSGallery'
$script:PSGalleryLocation = 'https://www.powershellgallery.com/api/v2'
-$script:PoshTestGalleryName = 'PoshTestGallery'
-$script:PostTestGalleryLocation = 'https://www.poshtestgallery.com/api/v2'
-
$script:NuGetGalleryName = 'NuGetGallery'
+$script:NuGetGalleryLocation = 'https://api.nuget.org/v3/index.json'
if($script:IsInbox)
{
@@ -141,6 +139,12 @@ function Get-NuGetGalleryName
{
return $script:NuGetGalleryName
}
+
+function Get-NuGetGalleryLocation
+{
+ return $script:NuGetGalleryLocation
+}
+
function Get-PSGalleryName
{
return $script:PSGalleryName
@@ -149,15 +153,6 @@ function Get-PSGalleryName
function Get-PSGalleryLocation {
return $script:PSGalleryLocation
}
-
-function Get-PoshTestGalleryName {
- return $script:PoshTestGalleryName
-}
-
-function Get-PoshTestGalleryLocation {
- return $script:PostTestGalleryLocation
-}
-
function Get-NewTestDirs {
Param(
[string[]]
@@ -256,6 +251,18 @@ function Register-LocalRepos {
Write-Verbose("registered psgettestlocal, psgettestlocal2")
}
+function Register-PSGallery {
+ $PSGalleryRepoParams = @{
+ Name = $script:PSGalleryName
+ Uri = $script:PSGalleryLocation
+ Priority = 1
+ Trusted = $false
+ }
+ Register-PSResourceRepository @PSGalleryRepoParams
+
+ Write-Verbose("registered PSGallery")
+}
+
function Unregister-LocalRepos {
if(Get-PSResourceRepository -Name "psgettestlocal"){
Unregister-PSResourceRepository -Name "psgettestlocal"
@@ -386,6 +393,57 @@ function Get-ModuleResourcePublishedToLocalRepoTestDrive
Publish-PSResource -Path $publishModuleBase -Repository $repoName
}
+function Get-ModuleResourcePublishedToLocalRepoTestDrive
+{
+ Param(
+ [string]
+ $moduleName,
+
+ [string]
+ $repoName,
+
+ [string]
+ $packageVersion,
+
+ [string]
+ $prereleaseLabel,
+
+ [string[]]
+ $tags
+ )
+ Get-TestDriveSetUp
+
+ $publishModuleName = $moduleName
+ $publishModuleBase = Join-Path $script:testIndividualResourceFolder $publishModuleName
+ $null = New-Item -Path $publishModuleBase -ItemType Directory -Force
+
+ $version = $packageVersion
+ if (!$tags -or ($tags.Count -eq 0))
+ {
+ if (!$prereleaseLabel)
+ {
+ New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Description "$publishModuleName module"
+ }
+ else
+ {
+ New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Prerelease $prereleaseLabel -Description "$publishModuleName module"
+ }
+ }
+ else {
+ # tags is not null or is empty
+ if (!$prereleaseLabel)
+ {
+ New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Description "$publishModuleName module" -Tags $tags
+ }
+ else
+ {
+ New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Prerelease $prereleaseLabel -Description "$publishModuleName module" -Tags $tags
+ }
+ }
+
+ Publish-PSResource -Path $publishModuleBase -Repository $repoName
+}
+
function Register-LocalRepos {
$repoUriAddress = Join-Path -Path $TestDrive -ChildPath "testdir"
$null = New-Item $repoUriAddress -ItemType Directory -Force
diff --git a/test/SavePSResourceTests/SavePSResourceAATests.ps1 b/test/SavePSResourceTests/SavePSResourceAATests.ps1
new file mode 100644
index 000000000..7e9e9f3d7
--- /dev/null
+++ b/test/SavePSResourceTests/SavePSResourceAATests.ps1
@@ -0,0 +1,163 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+<# WIP
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Save-PSResource for Azure Artifacts' {
+
+ BeforeAll {
+ $AzureArtifactsName = "AzureArtifacts"
+ $testModuleName = "test_module"
+ Get-NewPSResourceRepositoryFile
+ Register-PSResourceRepository -Name $AzureArtifactsName -Uri "https://pkgs.dev.azure.com/PowerShellGetTesting/PSGetTesting/_packaging/PSGetTest/nuget/v3/index.json"
+
+ $SaveDir = Join-Path $TestDrive 'SavedResources'
+ New-Item -Item Directory $SaveDir -Force
+ }
+
+ AfterEach {
+ # Delete contents of save directory
+ Remove-Item -Path (Join-Path $SaveDir '*') -Recurse -Force -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Save specific module resource by name" {
+ Save-PSResource -Name $testModuleName -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem $pkgDir.FullName).Count | Should -Be 1
+ }
+
+### TODO: Fix this test
+ It "Save multiple resources by name" {
+ $pkgNames = @($testModuleName, $testModuleName2)
+ Save-PSResource -Name $pkgNames -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq $testModuleName -or $_.Name -eq $testModuleName2 }
+ $pkgDirs.Count | Should -Be 2
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -Be 1
+ (Get-ChildItem $pkgDirs[1].FullName).Count | Should -Be 1
+ }
+
+ It "Should not save resource given nonexistant name" {
+ Save-PSResource -Name NonExistentModule -Repository $AzureArtifactsRepo -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "NonExistentModule"
+ $pkgDir.Name | Should -BeNullOrEmpty
+ }
+
+ It "Not Save module with Name containing wildcard" {
+ Save-PSResource -Name "TestModule*" -Repository $AzureArtifactsName -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "NameContainsWildcard,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Should save resource given name and exact version" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+ It "Should save resource given name and exact version with bracket syntax" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+ It "Should save resource given name and exact range inclusive [1.0.0, 3.0.0]" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0, 3.0.0]" -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0"
+ }
+
+ It "Should save resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Save-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0"
+ }
+
+ It "Should not save resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version="(1.0.0.0)"
+ try {
+ Save-PSResource -Name $testModuleName -Version $Version -Repository $AzureArtifactsName -Path $SaveDir -ErrorAction SilentlyContinue -TrustRepository
+ }
+ catch
+ {}
+
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -BeNullOrEmpty
+ $Error.Count | Should -Not -Be 0
+ $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+### TODO: FIX this text
+ It "Should not save resource with incorrectly formatted version such as version formatted with invalid delimiter [1-0-0-0]"{
+ $Version = "[1-0-0-0]"
+
+ try {
+ Save-PSResource -Name $testModuleName -Version $Version -Repository $AzureArtifactsName -Path $SaveDir -ErrorAction SilentlyContinue -TrustRepository
+ }
+ catch
+ {}
+
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -BeNullOrEmpty
+ $Error.Count | Should -Not -Be 0
+ $Error[0].FullyQualifiedErrorId | Should -BeExactly "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Save resource with latest (including prerelease) version given Prerelease parameter" {
+ Save-PSResource -Name $testModuleName -Prerelease -Repository $AzureArtifactsName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "5.2.5"
+ }
+
+ It "Save PSResourceInfo object piped in for prerelease version object" {
+ Find-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $AzureArtifactsName | Save-PSResource -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem -Path $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save module as a nupkg" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $AzureArtifactsName -Path $SaveDir -AsNupkg -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "test_module.1.0.0.nupkg"
+ $pkgDir | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module and include XML metadata file" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $AzureArtifactsName -Path $SaveDir -IncludeXml -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ $xmlFile = Get-ChildItem -Path $pkgDirVersion.FullName | Where-Object Name -eq "PSGetModuleInfo.xml"
+ $xmlFile | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module using -PassThru" {
+ $res = Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $AzureArtifactsName -Path $SaveDir -PassThru -TrustRepository
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0"
+ }
+
+ # Save module that is not authenticode signed
+ # Should FAIL to save the module
+ It "Save module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ { Save-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $AzureArtifactsName -TrustRepository -Path $SaveDir -ErrorAction SilentlyContinue } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+}
+#>
\ No newline at end of file
diff --git a/test/SavePSResourceTests/SavePSResourceLocalTests.ps1 b/test/SavePSResourceTests/SavePSResourceLocalTests.ps1
new file mode 100644
index 000000000..63071cf0d
--- /dev/null
+++ b/test/SavePSResourceTests/SavePSResourceLocalTests.ps1
@@ -0,0 +1,140 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Save-PSResource for local repositories' {
+
+ BeforeAll {
+ $localRepo = "psgettestlocal"
+ $moduleName = "test_local_mod"
+ $moduleName2 = "test_local_mod2"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "3.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "5.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName2 $localRepo "5.0.0"
+
+
+ $SaveDir = Join-Path $TestDrive 'SavedResources'
+ New-Item -Item Directory $SaveDir -Force
+ }
+
+ AfterEach {
+ # Delete contents of save directory
+ Remove-Item -Path (Join-Path $SaveDir '*') -Recurse -Force -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Save specific module resource by name" {
+ Save-PSResource -Name $moduleName -Repository $localRepo -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save multiple resources by name" {
+ $pkgNames = @($moduleName, $moduleName2)
+ Save-PSResource -Name $pkgNames -Repository $localRepo -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq $moduleName -or $_.Name -eq $moduleName2 }
+ $pkgDirs.Count | Should -Be 2
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -Be 1
+ (Get-ChildItem $pkgDirs[1].FullName).Count | Should -Be 1
+ }
+
+ It "Should not save resource given nonexistant name" {
+ Save-PSResource -Name NonExistentModule -Repository $localRepo -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "NonExistentModule"
+ $pkgDir.Name | Should -BeNullOrEmpty
+ }
+
+ It "Should save resource given name and exact version" {
+ Save-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+ It "Should save resource given name and exact version with bracket syntax" {
+ Save-PSResource -Name $moduleName -Version "[1.0.0]" -Repository $localRepo -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+# It "Should save resource given name and exact range inclusive [1.0.0, 3.0.0]" {
+# Save-PSResource -Name $moduleName -Version "[1.0.0, 3.0.0]" -Repository $localRepo -Path $SaveDir -TrustRepository
+# $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+# $pkgDir | Should -Not -BeNullOrEmpty
+# $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+# $pkgDirVersion.Name | Should -Be "3.0.0"
+# }
+
+ It "Should save resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Save-PSResource -Name $moduleName -Version "(1.0.0, 5.0.0)" -Repository $localRepo -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0"
+ }
+
+ It "Should not save resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version="(1.0.0.0)"
+ try {
+ Save-PSResource -Name $moduleName -Version $Version -Repository $localRepo -Path $SaveDir -ErrorAction SilentlyContinue -TrustRepository
+ }
+ catch
+ {}
+
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -BeNullOrEmpty
+ $Error.Count | Should -Not -Be 0
+ $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Save PSResourceInfo object piped in for prerelease version object" {
+ Find-PSResource -Name $moduleName -Version "5.0.0" -Repository $localRepo | Save-PSResource -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem -Path $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save module as a nupkg" {
+ Save-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -Path $SaveDir -AsNupkg -TrustRepository -WarningVariable WarningVar -warningaction SilentlyContinue
+ $WarningVar | Should -Not -BeNullOrEmpty
+ # not yet implemented
+ # $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "$moduleName.1.0.0.nupkg"
+ # $pkgDir | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module and include XML metadata file" {
+ Save-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -Path $SaveDir -IncludeXml -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $moduleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ $xmlFile = Get-ChildItem -Path $pkgDirVersion.FullName | Where-Object Name -eq "PSGetModuleInfo.xml"
+ $xmlFile | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module using -PassThru" {
+ $res = Save-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -Path $SaveDir -PassThru -TrustRepository
+ $res.Name | Should -Be $moduleName
+ $res.Version | Should -Be "1.0.0.0"
+ }
+
+ # Save module that is not authenticode signed
+ # Should FAIL to save the module
+ It "Save module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ { Save-PSResource -Name $moduleName -Version "5.0.0" -AuthenticodeCheck -Repository $localRepo -TrustRepository -Path $SaveDir -ErrorAction SilentlyContinue } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+ #>
+}
\ No newline at end of file
diff --git a/test/SavePSResourceTests/SavePSResourceV2Tests.ps1 b/test/SavePSResourceTests/SavePSResourceV2Tests.ps1
new file mode 100644
index 000000000..daba6ce8c
--- /dev/null
+++ b/test/SavePSResourceTests/SavePSResourceV2Tests.ps1
@@ -0,0 +1,172 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Save-PSResource for V2 Server Protocol' {
+
+ BeforeAll {
+ $PSGalleryName = Get-PSGalleryName
+ $testModuleName = "test_module"
+ $testScriptName = "test_script"
+ $testModuleName2 = "testmodule99"
+ $PackageManagement = "PackageManagement"
+ Get-NewPSResourceRepositoryFile
+
+ $SaveDir = Join-Path $TestDrive 'SavedResources'
+ New-Item -Item Directory $SaveDir -Force
+ }
+
+ AfterEach {
+ # Delete contents of save directory
+ Remove-Item -Path (Join-Path $SaveDir '*') -Recurse -Force -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Save specific module resource by name" {
+ Save-PSResource -Name $testModuleName -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save specific script resource by name" {
+ Save-PSResource -Name $testScriptName -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "test_script.ps1"
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save multiple resources by name" {
+ $pkgNames = @($testModuleName, $testModuleName2)
+ Save-PSResource -Name $pkgNames -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq $testModuleName -or $_.Name -eq $testModuleName2 }
+ $pkgDirs.Count | Should -Be 2
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -Be 1
+ (Get-ChildItem $pkgDirs[1].FullName).Count | Should -Be 1
+ }
+
+ It "Should not save resource given nonexistant name" {
+ Save-PSResource -Name NonExistentModule -Repository $PSGalleryName -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "NonExistentModule"
+ $pkgDir.Name | Should -BeNullOrEmpty
+ }
+
+ It "Not Save module with Name containing wildcard" {
+ Save-PSResource -Name "TestModule*" -Repository $PSGalleryName -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "NameContainsWildcard,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Should save resource given name and exact version" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0.0"
+ }
+
+ It "Should save resource given name and exact version with bracket syntax" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0.0"
+ }
+
+ It "Should save resource given name and exact range inclusive [1.0.0, 3.0.0]" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0, 3.0.0]" -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0.0"
+ }
+
+ It "Should save resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Save-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0.0"
+ }
+
+ It "Should not save resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version="(1.0.0.0)"
+ try {
+ Save-PSResource -Name $testModuleName -Version $Version -Repository $PSGalleryName -Path $SaveDir -ErrorAction SilentlyContinue -TrustRepository
+ }
+ catch
+ {}
+
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -BeNullOrEmpty
+ $Error.Count | Should -Not -Be 0
+ $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Save resource with latest (including prerelease) version given Prerelease parameter" {
+ Save-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "5.2.5"
+ }
+
+ It "Save a module with a dependency" {
+ Save-PSResource -Name "TestModuleWithDependencyE" -Version "1.0.0.0" -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq "TestModuleWithDependencyE" -or $_.Name -eq "TestModuleWithDependencyC" -or $_.Name -eq "TestModuleWithDependencyB" -or $_.Name -eq "TestModuleWithDependencyD"}
+ $pkgDirs.Count | Should -BeGreaterThan 1
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -BeGreaterThan 0
+ (Get-ChildItem $pkgDirs[1].FullName).Count | Should -BeGreaterThan 0
+ (Get-ChildItem $pkgDirs[2].FullName).Count | Should -BeGreaterThan 0
+ (Get-ChildItem $pkgDirs[3].FullName).Count | Should -BeGreaterThan 0
+ }
+
+ It "Save a module with a dependency and skip saving the dependency" {
+ Save-PSResource -Name "TestModuleWithDependencyE" -Version "1.0.0.0" -SkipDependencyCheck -Repository $PSGalleryName -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq "TestModuleWithDependencyE"}
+ $pkgDirs.Count | Should -Be 1
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -Be 1
+ }
+
+ It "Save PSResourceInfo object piped in for prerelease version object" {
+ Find-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName | Save-PSResource -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem -Path $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save module as a nupkg" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -Path $SaveDir -AsNupkg -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "test_module.1.0.0.nupkg"
+ $pkgDir | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module and include XML metadata file" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -Path $SaveDir -IncludeXml -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0.0"
+ $xmlFile = Get-ChildItem -Path $pkgDirVersion.FullName | Where-Object Name -eq "PSGetModuleInfo.xml"
+ $xmlFile | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module using -PassThru" {
+ $res = Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $PSGalleryName -Path $SaveDir -PassThru -TrustRepository
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0.0"
+ }
+
+ # Save module that is not authenticode signed
+ # Should FAIL to save the module
+ It "Save module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ Save-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+}
\ No newline at end of file
diff --git a/test/SavePSResourceTests/SavePSResourceV3Tests.ps1 b/test/SavePSResourceTests/SavePSResourceV3Tests.ps1
new file mode 100644
index 000000000..945685c3c
--- /dev/null
+++ b/test/SavePSResourceTests/SavePSResourceV3Tests.ps1
@@ -0,0 +1,146 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Save-PSResource for V3 Server Protocol' {
+
+ BeforeAll {
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $testModuleName = "test_module"
+ $testModuleName2 = "test_module2"
+ Get-NewPSResourceRepositoryFile
+
+ $SaveDir = Join-Path $TestDrive 'SavedResources'
+ New-Item -Item Directory $SaveDir -Force
+ }
+
+ AfterEach {
+ # Delete contents of save directory
+ Remove-Item -Path (Join-Path $SaveDir '*') -Recurse -Force -ErrorAction SilentlyContinue
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Save specific module resource by name" {
+ Save-PSResource -Name $testModuleName -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save multiple resources by name" {
+ $pkgNames = @($testModuleName, $testModuleName2)
+ Save-PSResource -Name $pkgNames -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDirs = Get-ChildItem -Path $SaveDir | Where-Object { $_.Name -eq $testModuleName -or $_.Name -eq $testModuleName2 }
+ $pkgDirs.Count | Should -Be 2
+ (Get-ChildItem $pkgDirs[0].FullName).Count | Should -Be 1
+ (Get-ChildItem $pkgDirs[1].FullName).Count | Should -Be 1
+ }
+
+ It "Should not save resource given nonexistant name" {
+ Save-PSResource -Name NonExistentModule -Repository $NuGetGalleryName -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "NonExistentModule"
+ $pkgDir.Name | Should -BeNullOrEmpty
+ }
+
+ It "Not Save module with Name containing wildcard" {
+ Save-PSResource -Name "TestModule*" -Repository $NuGetGalleryName -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue -TrustRepository
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "NameContainsWildcard,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Should save resource given name and exact version" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+ It "Should save resource given name and exact version with bracket syntax" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0]" -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ }
+
+ It "Should save resource given name and exact range inclusive [1.0.0, 3.0.0]" {
+ Save-PSResource -Name $testModuleName -Version "[1.0.0, 3.0.0]" -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0"
+ }
+
+ It "Should save resource given name and exact range exclusive (1.0.0, 5.0.0)" {
+ Save-PSResource -Name $testModuleName -Version "(1.0.0, 5.0.0)" -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "3.0.0"
+ }
+
+ It "Should not save resource with incorrectly formatted version such as exclusive version (1.0.0.0)" {
+ $Version="(1.0.0.0)"
+ try {
+ Save-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -Path $SaveDir -ErrorAction SilentlyContinue -TrustRepository
+ }
+ catch
+ {}
+
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -BeNullOrEmpty
+ $Error.Count | Should -Not -Be 0
+ $Error[0].FullyQualifiedErrorId | Should -Be "IncorrectVersionFormat,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+
+ It "Save resource with latest (including prerelease) version given Prerelease parameter" {
+ Save-PSResource -Name $testModuleName -Prerelease -Repository $NuGetGalleryName -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "5.2.5"
+ }
+
+ It "Save PSResourceInfo object piped in for prerelease version object" {
+ Find-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $NuGetGalleryName | Save-PSResource -Path $SaveDir -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ (Get-ChildItem -Path $pkgDir.FullName).Count | Should -Be 1
+ }
+
+ It "Save module as a nupkg" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -Path $SaveDir -AsNupkg -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "test_module.1.0.0.nupkg"
+ $pkgDir | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module and include XML metadata file" {
+ Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -Path $SaveDir -IncludeXml -TrustRepository
+ $pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq $testModuleName
+ $pkgDir | Should -Not -BeNullOrEmpty
+ $pkgDirVersion = Get-ChildItem -Path $pkgDir.FullName
+ $pkgDirVersion.Name | Should -Be "1.0.0"
+ $xmlFile = Get-ChildItem -Path $pkgDirVersion.FullName | Where-Object Name -eq "PSGetModuleInfo.xml"
+ $xmlFile | Should -Not -BeNullOrEmpty
+ }
+
+ It "Save module using -PassThru" {
+ $res = Save-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -Path $SaveDir -PassThru -TrustRepository
+ $res.Name | Should -Be $testModuleName
+ $res.Version | Should -Be "1.0.0"
+ }
+
+ # Save module that is not authenticode signed
+ # Should FAIL to save the module
+ It "Save module that is not authenticode signed" -Skip:(!(Get-IsWindows)) {
+ Save-PSResource -Name $testModuleName -Version "5.0.0" -AuthenticodeCheck -Repository $NuGetGalleryName -TrustRepository -Path $SaveDir -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.SavePSResource"
+ }
+}
\ No newline at end of file
diff --git a/test/UpdatePSResourceTests/UpdatePSResourceAATests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceAATests.ps1
new file mode 100644
index 000000000..be0e1c27e
--- /dev/null
+++ b/test/UpdatePSResourceTests/UpdatePSResourceAATests.ps1
@@ -0,0 +1,296 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+<# WIP
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Update-PSResource for Azure Artifacts' {
+
+ BeforeAll{
+ $AzureArtifactsName = "AzureArtifacts"
+ $testModuleName = "test_module"
+ Get-NewPSResourceRepositoryFile
+ Register-PSResourceRepository -Name $AzureArtifactsName -Uri "https://pkgs.dev.azure.com/PowerShellGetTesting/PSGetTesting/_packaging/PSGetTest/nuget/v3/index.json"
+
+ $SaveDir = Join-Path $TestDrive 'SavedResources'
+ New-Item -Item Directory $SaveDir -Force
+
+ AfterEach {
+ Uninstall-PSResource $testModuleName -Version "*"
+ }
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Update resource installed given Name parameter" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ Update-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ It "Update resource installed given Name and Version (specific) parameters" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ Update-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -eq [System.Version]"5.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -BeTrue
+ }
+
+ $testCases2 = @{Version="[3.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, exact match"},
+ @{Version="3.0.0"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, exact match without bracket syntax"},
+ @{Version="[3.0.0, 5.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range inclusive"},
+ @{Version="(3.0.0, 6.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range exclusive"},
+ @{Version="(3.0.0,)"; ExpectedVersions=@("1.0.0", "5.0.0"); Reason="validate version, minimum version exclusive"},
+ @{Version="[3.0.0,)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, minimum version inclusive"},
+ @{Version="(,5.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, maximum version exclusive"},
+ @{Version="(,5.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, maximum version inclusive"},
+ @{Version="[1.0.0, 5.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
+ @{Version="(1.0.0, 3.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
+
+ It "Update resource when given Name to " -TestCases $testCases2{
+ param($Version, $ExpectedVersions)
+
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ foreach ($item in $res) {
+ $item.Name | Should -Be $testModuleName
+ $ExpectedVersions | Should -Contain $item.Version
+ }
+ }
+
+ $testCases = @(
+ @{Version='(3.0.0.0)'; Description="exclusive version (3.0.0.0)"},
+ @{Version='[3-0-0-0]'; Description="version formatted with invalid delimiter [3-0-0-0]"}
+ )
+ It "Should not update resource with incorrectly formatted version such as " -TestCases $testCases{
+ param($Version, $Description)
+
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -TrustRepository 2>$null
+
+ $res = Get-PSResource -Name $testModuleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource with latest (including prerelease) version given Prerelease parameter" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Prerelease -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -ge [System.Version]"5.2.5")
+ {
+ $pkg.Prerelease | Should -Be "alpha001"
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under CurrentUser scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ # TODO: perhaps also install TestModule with the highest version (the one above 1.2.0.0) to the AllUsers path too
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under AllUsers scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name "testmodule99" -Version "0.0.93" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-Module -Name "testmodule99" -ListAvailable
+ $res | Should -Not -BeNullOrEmpty
+ $res.Version | Should -Contain "0.0.93"
+ }
+
+ # Windows only
+ It "Update resource under no specified scope" -skip:(!$IsWindows) {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $NuGetGalleryName -TrustRepository -verbose
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ # this line is commented out because AllUsers scope requires sudo and that isn't supported in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ # this test is skipped because it requires sudo to run and has yet to be resolved in CI
+ It "Update resource under AllUsers scope - Unix only" -Skip:($true) {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("usr") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ # this is commented out because it requires sudo to run with AllUsers scope and this hasn't been resolved in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # It "update resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "TestModuleWithLicense" -Version "0.0.1.0" -Repository $TestGalleryName -AcceptLicense
+ # Update-PSResource -Name "TestModuleWithLicense" -Repository $TestGalleryName -AcceptLicense
+ # $res = Get-PSResource "TestModuleWithLicense"
+
+ # $isPkgUpdated = $false
+ # foreach ($pkg in $res)
+ # {
+ # if ([System.Version]$pkg.Version -gt [System.Version]"0.0.1.0")
+ # {
+ # $isPkgUpdated = $true
+ # }
+ # }
+
+ # $isPkgUpdated | Should -Be $true
+ # }
+
+ It "Update module using -WhatIf, should not update the module" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -WhatIf -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource installed given -Name and -PassThru parameters" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Update-PSResource -Name $testModuleName -Version "3.0.0" -Repository $NuGetGalleryName -TrustRepository -PassThru
+ $res.Name | Should -Contain $testModuleName
+ $res.Version | Should -Contain "3.0.0"
+ }
+}
+#>
\ No newline at end of file
diff --git a/test/UpdatePSResourceTests/UpdatePSResourceLocalTests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceLocalTests.ps1
new file mode 100644
index 000000000..fbf6344b8
--- /dev/null
+++ b/test/UpdatePSResourceTests/UpdatePSResourceLocalTests.ps1
@@ -0,0 +1,275 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$ProgressPreference = "SilentlyContinue"
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test Update-PSResource for local repositories' {
+
+
+ BeforeAll {
+ $localRepo = "psgettestlocal"
+ $moduleName = "test_local_mod"
+ $moduleName2 = "test_local_mod2"
+ Get-NewPSResourceRepositoryFile
+ Register-LocalRepos
+
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "3.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName $localRepo "5.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName2 $localRepo "1.0.0"
+ Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName2 $localRepo "5.0.0"
+ }
+
+ AfterEach {
+ Uninstall-PSResource $moduleName, $moduleName2 -Version "*"
+ }
+
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Update resource installed given Name parameter" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ It "Update resources installed given Name (with wildcard) parameter" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+ Install-PSResource -Name $moduleName2 -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name "test_local*" -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name "test_local*" -Version "5.0.0"
+
+ $inputHashtable = @{test_module = "1.0.0"; test_module2 = "1.0.0"}
+ $isTest_ModuleUpdated = $false
+ $isTest_Module2Updated = $false
+ foreach ($item in $res)
+ {
+ if ([System.Version]$item.Version -gt [System.Version]$inputHashtable[$item.Name])
+ {
+ if ($item.Name -like $moduleName)
+ {
+ $isTest_ModuleUpdated = $true
+ }
+ elseif ($item.Name -like $moduleName2)
+ {
+ $isTest_Module2Updated = $true
+ }
+ }
+ }
+
+ $isTest_ModuleUpdated | Should -BeTrue
+ $isTest_Module2Updated | Should -BeTrue
+ }
+
+ It "Update resource installed given Name and Version (specific) parameters" {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository
+
+ Update-PSResource -Name $moduleName -Version "5.0.0" -Repository $localRepo -TrustRepository
+ $res = Get-PSResource -Name $moduleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -eq [System.Version]"5.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -BeTrue
+ }
+
+ # Windows only
+ It "update resource under CurrentUser scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ # TODO: perhaps also install TestModule with the highest version (the one above 1.2.0.0) to the AllUsers path too
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Version "3.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under AllUsers scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ Install-PSResource -Name $moduleName -Version "1.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ $res = Get-Module -Name $moduleName -ListAvailable
+ $res | Should -Not -BeNullOrEmpty
+ $res.Version | Should -Contain "5.0.0"
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.ModuleBase.Contains("Program") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+ $isPkgUpdated | Should -Be $true
+
+ }
+
+ # Windows only
+ It "Update resource under no specified scope" -skip:(!$IsWindows) {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+ Update-PSResource -Name $moduleName -Version "5.0.0.0" -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ # this line is commented out because AllUsers scope requires sudo and that isn't supported in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ # this test is skipped because it requires sudo to run and has yet to be resolved in CI
+ It "Update resource under AllUsers scope - Unix only" -Skip:($true) {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope AllUsers
+
+ Update-PSResource -Name $moduleName -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("usr") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ # this is commented out because it requires sudo to run with AllUsers scope and this hasn't been resolved in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $moduleName -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # It "update resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "TestModuleWithLicense" -Version "0.0.1.0" -Repository $TestGalleryName -AcceptLicense
+ # Update-PSResource -Name "TestModuleWithLicense" -Repository $TestGalleryName -AcceptLicense
+ # $res = Get-PSResource "TestModuleWithLicense"
+
+ # $isPkgUpdated = $false
+ # foreach ($pkg in $res)
+ # {
+ # if ([System.Version]$pkg.Version -gt [System.Version]"0.0.1.0")
+ # {
+ # $isPkgUpdated = $true
+ # }
+ # }
+
+ # $isPkgUpdated | Should -Be $true
+ # }
+
+ It "Update module using -WhatIf, should not update the module" {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+ Update-PSResource -Name $moduleName -WhatIf -Repository $localRepo -TrustRepository
+
+ $res = Get-PSResource -Name $moduleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource installed given -Name and -PassThru parameters" {
+ Install-PSResource -Name $moduleName -Version "1.0.0.0" -Repository $localRepo -TrustRepository
+
+ $res = Update-PSResource -Name $moduleName -Version "5.0.0.0" -Repository $localRepo -TrustRepository -PassThru
+ $res.Name | Should -Contain $moduleName
+ $res.Version | Should -Be "5.0.0.0"
+ }
+}
diff --git a/test/UpdatePSResource.Tests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceV2Tests.ps1
similarity index 86%
rename from test/UpdatePSResource.Tests.ps1
rename to test/UpdatePSResourceTests/UpdatePSResourceV2Tests.ps1
index b0c45c17d..5ddb55bb9 100644
--- a/test/UpdatePSResource.Tests.ps1
+++ b/test/UpdatePSResourceTests/UpdatePSResourceV2Tests.ps1
@@ -2,9 +2,9 @@
# Licensed under the MIT License.
$ProgressPreference = "SilentlyContinue"
-Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
-Describe 'Test Update-PSResource' {
+Describe 'Test HTTP Update-PSResource for V2 Server Protocol' {
BeforeAll {
@@ -19,16 +19,16 @@ Describe 'Test Update-PSResource' {
}
AfterEach {
- Uninstall-PSResource "test_module", "TestModule99", "TestModuleWithLicense", "test_module2", "test_script", "PackaeManagement" -Version "*"
+ Uninstall-PSResource "test_module", "TestModule99", "TestModuleWithLicense", "test_module2", "test_script" -Version "*"
}
AfterAll {
Get-RevertPSResourceRepositoryFile
}
- It "update resource installed given Name parameter" {
+ It "Update resource installed given Name parameter" {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
-
+
Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
$res = Get-PSResource -Name $testModuleName
@@ -44,7 +44,7 @@ Describe 'Test Update-PSResource' {
$isPkgUpdated | Should -Be $true
}
- It "update resources installed given Name (with wildcard) parameter" {
+ It "Update resources installed given Name (with wildcard) parameter" {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
Install-PSResource -Name $testModuleName2 -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
@@ -73,7 +73,7 @@ Describe 'Test Update-PSResource' {
$isTest_Module2Updated | Should -BeTrue
}
- It "update resource installed given Name and Version (specific) parameters" {
+ It "Update resource installed given Name and Version (specific) parameters" {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
Update-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository
@@ -101,7 +101,7 @@ Describe 'Test Update-PSResource' {
@{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
@{Version="(1.0.0.0, 3.0.0.0]"; ExpectedVersions=@("1.0.0.0", "3.0.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
- It "update resource when given Name to " -TestCases $testCases2{
+ It "Update resource when given Name to " -TestCases $testCases2{
param($Version, $ExpectedVersions)
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
@@ -138,7 +138,7 @@ Describe 'Test Update-PSResource' {
$isPkgUpdated | Should -Be $false
}
- It "update resource with latest (including prerelease) version given Prerelease parameter" {
+ It "Update resource with latest (including prerelease) version given Prerelease parameter" {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
Update-PSResource -Name $testModuleName -Prerelease -Repository $PSGalleryName -TrustRepository
$res = Get-PSResource -Name $testModuleName
@@ -156,8 +156,8 @@ Describe 'Test Update-PSResource' {
$isPkgUpdated | Should -Be $true
}
- # Windows only
- It "update resource under CurrentUser scope" -skip:(!$IsWindows) {
+ # Windows only
+ It "update resource under CurrentUser scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
# TODO: perhaps also install TestModule with the highest version (the one above 1.2.0.0) to the AllUsers path too
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope AllUsers
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
@@ -178,13 +178,13 @@ Describe 'Test Update-PSResource' {
$isPkgUpdated | Should -Be $true
}
-
+
# Windows only
It "update resource under AllUsers scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
- Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose
- Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser -Verbose
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
- Update-PSResource -Name "testmodule99" -Version "0.0.93" -Repository $PSGalleryName -TrustRepository -Scope AllUsers -Verbose
+ Update-PSResource -Name "testmodule99" -Version "0.0.93" -Repository $PSGalleryName -TrustRepository -Scope AllUsers
$res = Get-Module -Name "testmodule99" -ListAvailable
$res | Should -Not -BeNullOrEmpty
@@ -192,9 +192,9 @@ Describe 'Test Update-PSResource' {
}
# Windows only
- It "update resource under no specified scope" -skip:(!$IsWindows) {
+ It "Update resource under no specified scope" -skip:(!$IsWindows) {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
- Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $PSGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $PSGalleryName -TrustRepository -verbose
$res = Get-PSResource -Name $testModuleName
@@ -318,7 +318,7 @@ Describe 'Test Update-PSResource' {
$isPkgUpdated | Should -Be $false
}
- It "update resource installed given -Name and -PassThru parameters" {
+ It "Update resource installed given -Name and -PassThru parameters" {
Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository
$res = Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $PSGalleryName -TrustRepository -PassThru
@@ -326,17 +326,27 @@ Describe 'Test Update-PSResource' {
$res.Version | Should -Contain "3.0.0.0"
}
+ ## TODO update this -- find module with valid catalog file
# Update to module 1.4.3 (is authenticode signed and has catalog file)
- # Should update successfully
+ # Should update successfully
It "Update module with catalog file using publisher validation" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $PackageManagement -Version "1.4.2" -Repository $PSGalleryName -TrustRepository
- Update-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
+ Install-PSResource -Name $PackageManagement -Version "1.4.2" -Repository $PSGalleryName -TrustRepository -verbose
+ Update-PSResource -Name $PackageManagement -Version "1.4.3" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository
$res1 = Get-PSResource $PackageManagement -Version "1.4.3"
$res1.Name | Should -Be $PackageManagement
- $res1.Version | Should -Be "1.4.3.0"
+ $res1.Version | Should -Be "1.4.3"
}
+ # # Update to module 1.4.4.1 (with incorrect catalog file)
+ # # Should FAIL to update the module
+ # It "Update module with incorrect catalog file" -Skip:(!(Get-IsWindows)) {
+ # Install-PSResource -Name $PackageManagement -Version "1.4.2" -Reinstall -Repository $PSGalleryName -TrustRepository
+ # Update-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ # $err.Count | Should -Not -Be 0
+ # $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSResource"
+ # }
+
# Update to module 1.4.7 (is authenticode signed and has NO catalog file)
# Should update successfully
It "Install module with no catalog file" -Skip:(!(Get-IsWindows)) {
@@ -345,14 +355,7 @@ Describe 'Test Update-PSResource' {
$res1 = Get-PSResource $PackageManagement -Version "1.4.7"
$res1.Name | Should -Be $PackageManagement
- $res1.Version | Should -Be "1.4.7.0"
- }
-
- # Update to module 1.4.4.1 (with incorrect catalog file)
- # Should FAIL to update the module
- It "Update module with incorrect catalog file" -Skip:(!(Get-IsWindows)) {
- Install-PSResource -Name $PackageManagement -Version "1.4.2" -Repository $PSGalleryName -TrustRepository
- { Update-PSResource -Name $PackageManagement -Version "1.4.4.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "TestFileCatalogError,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSResource"
+ $res1.Version | Should -Be "1.4.7"
}
# Update script that is signed
@@ -363,13 +366,15 @@ Describe 'Test Update-PSResource' {
$res1 = Get-PSResource "Install-VSCode" -Version "1.4.2"
$res1.Name | Should -Be "Install-VSCode"
- $res1.Version | Should -Be "1.4.2.0"
+ $res1.Version | Should -Be "1.4.2"
}
# Update script that is not signed
# Should throw
It "Update script that is not signed" -Skip:(!(Get-IsWindows)) {
Install-PSResource -Name "TestTestScript" -Version "1.0" -Repository $PSGalleryName -TrustRepository
- { Update-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository } | Should -Throw -ErrorId "GetAuthenticodeSignatureError,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSResource"
+ Update-PSResource -Name "TestTestScript" -Version "1.3.1.1" -AuthenticodeCheck -Repository $PSGalleryName -TrustRepository -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSResource"
}
}
diff --git a/test/UpdatePSResourceTests/UpdatePSResourceV3Tests.ps1 b/test/UpdatePSResourceTests/UpdatePSResourceV3Tests.ps1
new file mode 100644
index 000000000..41f81fb33
--- /dev/null
+++ b/test/UpdatePSResourceTests/UpdatePSResourceV3Tests.ps1
@@ -0,0 +1,291 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$((Get-Item $psscriptroot).parent)\PSGetTestUtils.psm1" -Force
+
+Describe 'Test HTTP Update-PSResource for V3 Server Protocol' {
+
+ BeforeAll{
+ $NuGetGalleryName = Get-NuGetGalleryName
+ $testModuleName = "test_module"
+ Get-NewPSResourceRepositoryFile
+ }
+
+ AfterEach {
+ Uninstall-PSResource $testModuleName -Version "*"
+ }
+ AfterAll {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ It "Update resource installed given Name parameter" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ Update-PSResource -Name $testModuleName -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ It "Update resource installed given Name and Version (specific) parameters" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ Update-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -eq [System.Version]"5.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -BeTrue
+ }
+
+ $testCases2 = @{Version="[3.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, exact match"},
+ @{Version="3.0.0"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, exact match without bracket syntax"},
+ @{Version="[3.0.0, 5.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range inclusive"},
+ @{Version="(3.0.0, 6.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, exact range exclusive"},
+ @{Version="(3.0.0,)"; ExpectedVersions=@("1.0.0", "5.0.0"); Reason="validate version, minimum version exclusive"},
+ @{Version="[3.0.0,)"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, minimum version inclusive"},
+ @{Version="(,5.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, maximum version exclusive"},
+ @{Version="(,5.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0", "5.0.0"); Reason="validate version, maximum version inclusive"},
+ @{Version="[1.0.0, 5.0.0)"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed inclusive minimum and exclusive maximum version"}
+ @{Version="(1.0.0, 3.0.0]"; ExpectedVersions=@("1.0.0", "3.0.0"); Reason="validate version, mixed exclusive minimum and inclusive maximum version"}
+
+ It "Update resource when given Name to " -TestCases $testCases2{
+ param($Version, $ExpectedVersions)
+
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ foreach ($item in $res) {
+ $item.Name | Should -Be $testModuleName
+ $ExpectedVersions | Should -Contain $item.Version
+ }
+ }
+
+ $testCases = @(
+ @{Version='(3.0.0.0)'; Description="exclusive version (3.0.0.0)"},
+ @{Version='[3-0-0-0]'; Description="version formatted with invalid delimiter [3-0-0-0]"}
+ )
+ It "Should not update resource with incorrectly formatted version such as " -TestCases $testCases{
+ param($Version, $Description)
+
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version $Version -Repository $NuGetGalleryName -TrustRepository 2>$null
+
+ $res = Get-PSResource -Name $testModuleName
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource with latest (including prerelease) version given Prerelease parameter" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Prerelease -Repository $NuGetGalleryName -TrustRepository
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -ge [System.Version]"5.2.5")
+ {
+ $pkg.Prerelease | Should -Be "alpha001"
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under CurrentUser scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ # TODO: perhaps also install TestModule with the highest version (the one above 1.2.0.0) to the AllUsers path too
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Windows only
+ It "update resource under AllUsers scope" -skip:(!($IsWindows -and (Test-IsAdmin))) {
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name "testmodule99" -Version "0.0.91" -Repository $NuGetGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name "testmodule99" -Version "0.0.93" -Repository $NuGetGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-Module -Name "testmodule99" -ListAvailable
+ $res | Should -Not -BeNullOrEmpty
+ $res.Version | Should -Contain "0.0.93"
+ }
+
+ # Windows only
+ It "Update resource under no specified scope" -skip:(!$IsWindows) {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -Version "3.0.0.0" -Repository $NuGetGalleryName -TrustRepository -verbose
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("Documents") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under CurrentUser scope - Unix only" -Skip:(Get-IsWindows) {
+ # this line is commented out because AllUsers scope requires sudo and that isn't supported in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/usr/local/share/powershell/Modules'
+ # this test is skipped because it requires sudo to run and has yet to be resolved in CI
+ It "Update resource under AllUsers scope - Unix only" -Skip:($true) {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository -Scope AllUsers
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("usr") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # Unix only
+ # Expected path should be similar to: '/home/janelane/.local/share/powershell/Modules'
+ It "Update resource under no specified scope - Unix only" -Skip:(Get-IsWindows) {
+ # this is commented out because it requires sudo to run with AllUsers scope and this hasn't been resolved in CI yet
+ # Install-PSResource -Name "TestModule" -Version "1.1.0.0" -Repository $TestGalleryName -Scope AllUsers
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository -Scope CurrentUser
+
+ Update-PSResource -Name $testModuleName -Repository $PSGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $pkg.InstalledLocation.Contains("$env:HOME/.local") | Should -Be $true
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $true
+ }
+
+ # It "update resource that requires accept license with -AcceptLicense flag" {
+ # Install-PSResource -Name "TestModuleWithLicense" -Version "0.0.1.0" -Repository $TestGalleryName -AcceptLicense
+ # Update-PSResource -Name "TestModuleWithLicense" -Repository $TestGalleryName -AcceptLicense
+ # $res = Get-PSResource "TestModuleWithLicense"
+
+ # $isPkgUpdated = $false
+ # foreach ($pkg in $res)
+ # {
+ # if ([System.Version]$pkg.Version -gt [System.Version]"0.0.1.0")
+ # {
+ # $isPkgUpdated = $true
+ # }
+ # }
+
+ # $isPkgUpdated | Should -Be $true
+ # }
+
+ It "Update module using -WhatIf, should not update the module" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $NuGetGalleryName -TrustRepository
+ Update-PSResource -Name $testModuleName -WhatIf -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Get-PSResource -Name $testModuleName
+
+ $isPkgUpdated = $false
+ foreach ($pkg in $res)
+ {
+ if ([System.Version]$pkg.Version -gt [System.Version]"1.0.0.0")
+ {
+ $isPkgUpdated = $true
+ }
+ }
+
+ $isPkgUpdated | Should -Be $false
+ }
+
+ It "Update resource installed given -Name and -PassThru parameters" {
+ Install-PSResource -Name $testModuleName -Version "1.0.0" -Repository $NuGetGalleryName -TrustRepository
+
+ $res = Update-PSResource -Name $testModuleName -Version "3.0.0" -Repository $NuGetGalleryName -TrustRepository -PassThru
+ $res.Name | Should -Contain $testModuleName
+ $res.Version | Should -Contain "3.0.0"
+ }
+}
diff --git a/test/testRepositories.xml b/test/testRepositories.xml
index a0da40886..0b1895050 100644
--- a/test/testRepositories.xml
+++ b/test/testRepositories.xml
@@ -1,5 +1,5 @@
-
-
+
+