Skip to content

Allow .psd1 to be passed into -RequiredResourceFile #610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/code/GetHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class GetHelper

private readonly PSCmdlet _cmdletPassedIn;
private readonly Dictionary<string, PSResourceInfo> _scriptDictionary;
public const string PSScriptFileExt = ".ps1";

#endregion

Expand Down Expand Up @@ -222,7 +223,7 @@ private static string GetResourceNameFromPath(string path)
// ./Modules/Microsoft.PowerShell.Test-Module : Microsoft.PowerShell.Test-Module
// ./Scripts/Microsoft.PowerShell.Test-Script.ps1 : Microsoft.PowerShell.Test-Script
var resourceName = Path.GetFileName(path);
return Path.GetExtension(resourceName).Equals(".ps1", StringComparison.OrdinalIgnoreCase)
return Path.GetExtension(resourceName).Equals(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)
? Path.GetFileNameWithoutExtension(resourceName) : resourceName;
}

Expand Down
22 changes: 12 additions & 10 deletions src/code/InstallHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
/// Install helper class
/// </summary>
internal class InstallHelper : PSCmdlet
{
{
#region Members

public const string PSDataFileExt = ".psd1";
public const string PSScriptFileExt = ".ps1";
private const string MsgRepositoryNotTrusted = "Untrusted repository";
private const string MsgInstallUntrustedPackage = "You are installing the modules from an untrusted repository. If you trust this repository, change its Trusted value by running the Set-PSResourceRepository cmdlet. Are you sure you want to install the PSresource from '{0}' ?";

Expand Down Expand Up @@ -461,8 +463,8 @@ private List<PSResourceInfo> InstallPackage(
string tempDirNameVersion = isLocalRepo ? tempInstallPath : Path.Combine(tempInstallPath, pkgIdentity.Id.ToLower(), newVersion);
var version4digitNoPrerelease = pkgIdentity.Version.Version.ToString();
string moduleManifestVersion = string.Empty;
var scriptPath = Path.Combine(tempDirNameVersion, pkg.Name + ".ps1");
var modulePath = Path.Combine(tempDirNameVersion, pkg.Name + ".psd1");
var scriptPath = Path.Combine(tempDirNameVersion, pkg.Name + PSScriptFileExt);
var modulePath = Path.Combine(tempDirNameVersion, pkg.Name + PSDataFileExt);
// Check if the package is a module or a script
var isModule = File.Exists(modulePath);

Expand Down Expand Up @@ -497,7 +499,7 @@ private List<PSResourceInfo> InstallPackage(

if (isModule)
{
var moduleManifest = Path.Combine(tempDirNameVersion, pkgIdentity.Id + ".psd1");
var moduleManifest = Path.Combine(tempDirNameVersion, pkgIdentity.Id + PSDataFileExt);
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.", pkgIdentity.Id, moduleManifest);
Expand All @@ -509,7 +511,7 @@ private List<PSResourceInfo> InstallPackage(
continue;
}

if (!Utils.TryParseModuleManifest(moduleManifest, _cmdletPassedIn, out Hashtable parsedMetadataHashtable))
if (!Utils.TryParsePSDataFile(moduleManifest, _cmdletPassedIn, out Hashtable parsedMetadataHashtable))
{
// Ran into errors parsing the module manifest file which was found in Utils.ParseModuleManifest() and written.
continue;
Expand Down Expand Up @@ -878,16 +880,16 @@ private void MoveFilesIntoInstallPath(
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 + ".ps1"))));
if (File.Exists(Path.Combine(finalModuleVersionDir, pkgInfo.Name + ".ps1")))
_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 + ".ps1"));
File.Delete(Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
}
}

_cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + ".ps1")));
Utils.MoveFiles(scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + ".ps1"));
_cmdletPassedIn.WriteVerbose(string.Format("Moving '{0}' to '{1}'", scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt)));
Utils.MoveFiles(scriptPath, Path.Combine(finalModuleVersionDir, pkgInfo.Name + PSScriptFileExt));
}
}

Expand Down
70 changes: 62 additions & 8 deletions src/code/InstallPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class InstallPSResource : PSCmdlet
public PSResourceInfo InputObject { get; set; }

/// <summary>
/// Installs resources based on input from a JSON file.
/// Installs resources based on input from a .psd1 (hashtable) or .json file.
/// </summary>
[Parameter(ParameterSetName = RequiredResourceFileParameterSet)]
[ValidateNotNullOrEmpty]
Expand All @@ -138,13 +138,31 @@ public String RequiredResourceFile

if (!File.Exists(resolvedPath))
{
var exMessage = String.Format("The RequiredResourceFile does not exist. Please try specifying a path to a valid .json file");
var exMessage = String.Format("The RequiredResourceFile does not exist. Please try specifying a path to a valid .json or .psd1 file");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileDoesNotExist = new ErrorRecord(ex, "RequiredResourceFileDoesNotExist", ErrorCategory.ObjectNotFound, null);

ThrowTerminatingError(RequiredResourceFileDoesNotExist);
}

if (resolvedPath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase))
{
_resourceFileType = ResourceFileType.JsonFile;
}
else if (resolvedPath.EndsWith(".psd1", StringComparison.InvariantCultureIgnoreCase))
{
_resourceFileType = ResourceFileType.PSDataFile;
}
else
{
// Throw here because no further processing can be done.
var exMessage = String.Format("The RequiredResourceFile must have either a '.json' or '.psd1' extension. Please try specifying a path to a valid .json or .psd1 file");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileNotValid = new ErrorRecord(ex, "RequiredResourceFileNotValid", ErrorCategory.ObjectNotFound, null);

ThrowTerminatingError(RequiredResourceFileNotValid);
}

_requiredResourceFile = resolvedPath;
}
}
Expand Down Expand Up @@ -176,6 +194,17 @@ public String RequiredResourceFile

#endregion

#region Enums

private enum ResourceFileType
{
UnknownFile,
JsonFile,
PSDataFile
}

#endregion

#region Members

private const string NameParameterSet = "NameParameterSet";
Expand All @@ -188,6 +217,7 @@ public String RequiredResourceFile
private Hashtable _requiredResourceHash;
VersionRange _versionRange;
InstallHelper _installHelper;
ResourceFileType _resourceFileType;

#endregion

Expand Down Expand Up @@ -252,7 +282,7 @@ protected override void ProcessRecord()
break;

case RequiredResourceFileParameterSet:
/* json file contents should look like:
/* .json file contents should look like:
{
"Pester": {
"allowPrerelease": true,
Expand All @@ -262,28 +292,52 @@ protected override void ProcessRecord()
}
}
*/


/* .psd1 file contents should look like:
@{
"Configuration" = @{ version = "[4.4.2,4.7.0]" }
"Pester" = @{
version = "[4.4.2,4.7.0]"
repository = PSGallery
credential = $cred
prerelease = $true
}
}
*/

string requiredResourceFileStream = string.Empty;
using (StreamReader sr = new StreamReader(_requiredResourceFile))
{
requiredResourceFileStream = sr.ReadToEnd();
}

Hashtable pkgsInJsonFile = null;
Hashtable pkgsInFile = null;
try
{
pkgsInJsonFile = Utils.ConvertJsonToHashtable(this, requiredResourceFileStream);
if (_resourceFileType.Equals(ResourceFileType.JsonFile))
{
pkgsInFile = Utils.ConvertJsonToHashtable(this, requiredResourceFileStream);
}
else
{
// must be a .psd1 file
if (!Utils.TryParsePSDataFile(_requiredResourceFile, this, out pkgsInFile))
{
// Ran into errors parsing the .psd1 file which was found in Utils.TryParsePSDataFile() and written.
return;
}
}
}
catch (Exception)
{
var exMessage = String.Format("Argument for parameter -RequiredResourceFile is not in proper json format. Make sure argument is either a valid json file.");
var exMessage = String.Format("Argument for parameter -RequiredResourceFile is not in proper json or hashtable format. Make sure argument is either a valid .json or .psd1 file.");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileNotInProperJsonFormat = new ErrorRecord(ex, "RequiredResourceFileNotInProperJsonFormat", ErrorCategory.InvalidData, null);

ThrowTerminatingError(RequiredResourceFileNotInProperJsonFormat);
}

RequiredResourceHelper(pkgsInJsonFile);
RequiredResourceHelper(pkgsInFile);
break;

case RequiredResourceParameterSet:
Expand Down
12 changes: 7 additions & 5 deletions src/code/PublishPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public string Path
{
_path = resolvedPath;
}
else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase))
else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase))
{
_path = resolvedPath;
}
Expand Down Expand Up @@ -163,6 +163,8 @@ public PSCredential ProxyCredential {
private NuGetVersion _pkgVersion;
private string _pkgName;
private static char[] _PathSeparators = new [] { System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar };
public const string PSDataFileExt = ".psd1";
public const string PSScriptFileExt = ".ps1";

#endregion

Expand All @@ -179,7 +181,7 @@ protected override void ProcessRecord()
{
// Returns the name of the file or the name of the directory, depending on path
var pkgFileOrDir = new DirectoryInfo(_path);
bool isScript = _path.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase);
bool isScript = _path.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase);

if (!ShouldProcess(string.Format("Publish resource '{0}' from the machine", _path)))
{
Expand Down Expand Up @@ -233,7 +235,7 @@ protected override void ProcessRecord()
else
{
_pkgName = pkgFileOrDir.Name;
resourceFilePath = System.IO.Path.Combine(_path, _pkgName + ".psd1");
resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt);

// Validate that there's a module manifest
if (!File.Exists(resourceFilePath))
Expand Down Expand Up @@ -332,7 +334,7 @@ protected override void ProcessRecord()
if (isScript)
{
// copy the script file to the temp directory
File.Copy(_path, System.IO.Path.Combine(outputDir, _pkgName + ".ps1"), true);
File.Copy(_path, System.IO.Path.Combine(outputDir, _pkgName + PSDataFileExt), true);
}
else
{
Expand Down Expand Up @@ -470,7 +472,7 @@ private string CreateNuspec(
if (!isScript)
{
// Parse the module manifest and *replace* the passed-in metadata with the module manifest metadata.
if (!Utils.TryParseModuleManifest(
if (!Utils.TryParsePSDataFile(
moduleFileInfo: filePath,
cmdletPassedIn: this,
parsedMetadataHashtable: out parsedMetadataHash))
Expand Down
3 changes: 2 additions & 1 deletion src/code/UninstallPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public sealed class UninstallPSResource : PSCmdlet

private const string NameParameterSet = "NameParameterSet";
private const string InputObjectParameterSet = "InputObjectParameterSet";
public const string PSScriptFileExt = ".ps1";
public static readonly string OsPlatform = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
VersionRange _versionRange;
List<string> _pathsToSearch = new List<string>();
Expand Down Expand Up @@ -180,7 +181,7 @@ private bool UninstallPkgHelper()
}

ErrorRecord errRecord = null;
if (pkgPath.EndsWith(".ps1"))
if (pkgPath.EndsWith(PSScriptFileExt))
{
successfullyUninstalled = UninstallScriptHelper(pkgPath, pkgName, out errRecord);
}
Expand Down
8 changes: 4 additions & 4 deletions src/code/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ internal static class Utils
{
#region String fields

public static readonly string[] EmptyStrArray = Array.Empty<string>();

public static readonly string[] EmptyStrArray = Array.Empty<string>();
public const string PSDataFileExt = ".psd1";
private const string ConvertJsonToHashtableScript = @"
param (
[string] $json
Expand Down Expand Up @@ -728,7 +728,7 @@ private static void GetStandardPlatformPaths(

#region Manifest methods

public static bool TryParseModuleManifest(
public static bool TryParsePSDataFile(
string moduleFileInfo,
PSCmdlet cmdletPassedIn,
out Hashtable parsedMetadataHashtable)
Expand All @@ -738,7 +738,7 @@ public static bool TryParseModuleManifest(

// A script will already have the metadata parsed into the parsedMetadatahash,
// a module will still need the module manifest to be parsed.
if (moduleFileInfo.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))
if (moduleFileInfo.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase))
{
// Parse the module manifest
var ast = Parser.ParseFile(
Expand Down
23 changes: 21 additions & 2 deletions test/InstallPSResource.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Describe 'Test Install-PSResource for Module' {
$NuGetGalleryName = Get-NuGetGalleryName
$testModuleName = "TestModule"
$RequiredResourceJSONFileName = "TestRequiredResourceFile.json"
$RequiredResourcePSD1FileName = "TestRequiredResourceFile.psd1"
Get-NewPSResourceRepositoryFile
Register-LocalRepos
}
Expand Down Expand Up @@ -381,10 +382,28 @@ Describe 'Test Install-PSResource for Module' {
$res3.Version | Should -Be "0.0.5"
}

It "Install modules using -RequiredResourceFile with PSD1 file" {
$rrFilePSD1 = Join-Path -Path $psscriptroot -ChildPath $RequiredResourcePSD1FileName

Install-PSResource -RequiredResourceFile $rrFilePSD1

$res1 = Get-Module "TestModule" -ListAvailable
$res1.Name | Should -Be "TestModule"
$res1.Version | Should -Be "1.3.0"

$res2 = Get-Module "TestModulePrerelease" -ListAvailable
$res2.Name | Should -Be "TestModulePrerelease"
$res2.Version | Should -Be "0.0.1"

$res3 = Get-Module "TestModule99" -ListAvailable
$res3.Name | Should -Be "TestModule99"
$res3.Version | Should -Be "0.0.5"
}

It "Install modules using -RequiredResourceFile with JSON file" {
$rrFileJSON = ".\$RequiredResourceJSONFileName"
$rrFileJSON = Join-Path -Path $psscriptroot -ChildPath $RequiredResourceJSONFileName

Install-PSResource -RequiredResourceFile $rrFileJSON -Verbose
Install-PSResource -RequiredResourceFile $rrFileJSON

$res1 = Get-Module "TestModule" -ListAvailable
$res1.Name | Should -Be "TestModule"
Expand Down
3 changes: 1 addition & 2 deletions test/TestRequiredResourceFile.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
"repository": "PoshTestGallery",
"prerelease": "true"
},
"TestModule99": {
}
"TestModule99": {}
}
14 changes: 14 additions & 0 deletions test/TestRequiredResourceFile.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@{
TestModule = @{
version = "[0.0.1,1.3.0]"
repository = "PoshTestGallery"
}

TestModulePrerelease = @{
version = "[0.0.0,0.0.5]"
repository = "PoshTestGallery"
prerelease = "true"
}

TestModule99 = @{}
}