Skip to content
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

Updating the PowerShellGet 3.0 RFC #237

Closed
wants to merge 14 commits into from
Closed
393 changes: 393 additions & 0 deletions 2-Draft-Accepted/RFCxxxx-PowerShellGet-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,393 @@
---
RFC: RFCxxxx
Author: Sydney Smith
Status: Draft
SupercededBy: N/A
Version: 0.3
Area: PowerShellGet
Comments Due: 6/30
Plan to implement: Yes, PS7
---

# PowerShellGet 3.0 Module

PowerShellGet has enabled an active community to publish and install PowerShell
Modules.
However, it has some architectural and design limitations that prevent it from
moving forward more quickly.
This RFC proposes some significant changes to address this.

> [!NOTE]
> There are no intentions to break existing PSGet v2 users so existing
> users can stay on PSGet v2 and that will continue to work.

## Motivation

As a PowerShell user,
I can easily install modules without specifying lots of switches,
so that I can be more productive.

As a PowerShellGet contributor,
I can more easily contribute code,
so that PowerShellGet evolves more quickly.

## User Experience

```powershell
PS> Install-PSResource HappyDays
```

## Specification

### Rewrite of PowerShellGet

PowerShellGet is currently written as PowerShell script with a dependency on PackageManagement.
Proposal is to write PSResource in C# to reduce complexity and make easier to maintain.
In addition, remove dependency on PackageManagement completely as well as dependency on
nuget.exe.
This module will depend on Nuget Server and Client packages including https://www.nuget.org/packages/NuGet.Client.
This module would be shipped in the PSGallery supporting PowerShell 5.1+.
This module will likely ship in PowerShell 7.2 alongside PowerShellGet compatibility module.

### Side-by-side with PowerShellGet

Since the current PowerShellGet 2.x version is a script module and this new one
is a C# based module, they can coexist side-by-side.

Script and module metadata will retain compatible formatting as it exists with v2.

We will introduce a compatibility module that will allow users to use known cmdlets like Install-Module
from the PowerShellGet 3.0 implementation.
This module will ship separately from PowerShellGet 3.0 but is planned as a part of the transition effort.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where having a daily task of sorts would make sense - whether this be a scheduled task or a cronjob & is bundled into PowerShellGetv3 (or at least a doc page on this) would be a point to contend with.

Like the PowerShell Help system, can I ask that Update-PSCache will allow me to do an online update from all repositories by default, a specified repository & also allow me to update across the network from a downloaded cache file, as to reduce levels of network traffic to Repositories like the PowerShellGallery or internal Repositories.

### Repository management

`Register-PSResourceRepository` will allow for registering additional repositories.
A `-PSGallery` switch enables registering PSGallery should it be accidentally removed.
This would be in a different parameter set from `-URL` and `-Repository`.

The `-URL` will accept the HTTP address with support for v2 and v3 protocols.
Support for local filesystem repositories must be maintained.
A `-Trusted` switch indicates whether to prompt the user when installing resources
from that repository.
Trusted repositories automatically have a priority of 0.
`-Trusted` is a different parameter set from `-Priority`.

By default, if this switch is not specified, the repository is untrusted.
A `-Repository` parameter will accept an array of hashtables equivalent to
the parameter names (like splatting).

```powershell
Register-PSResourceRepository -Repository @(
@{ URL = "https://one.com"; Name = "One"; Trusted = $true; Credential = $cred }
@{ URL = "https://powershellgallery.com"; Name = "PSGallery"; Trusted = $true; Default = $true }
@{ URL = "\\server\share\myrepository"; Name="Enterprise"; Trusted = $true }
)
```

A `-Name` parameter allows for setting a friendly name.

A `-Priority` parameter allows setting the search order of repositories.
A lower value has higher priority.
If not specified, the default value is 50.
PSGallery will have a default value of 50.

`Get-PSResourceRepository` will list out the registered repositories.

`Set-PSResourceRepository` can be used to update a repository URL, trust level,
or priority.

`Unregister-PSResourceRepository` can be used to un-register a repository.

### Finding resources

Current design of PowerShellGet is to have different cmdlets for each of the
different resources types that it supports:

`Find-Command`
`Find-DscResource`
`Find-Module`
`Find-RoleCapability`
`Find-Script`

Instead, these will be abstracted by a `Find-PSResource` cmdlet.
A `-Type` parameter will accept an array of types to allow filtering.
Comment on lines +104 to +114

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the Install-Module comment in v3 GA you ideally should wrapper these as not to break the current user experience & whilst I dislike the use of Warning output that Az use, you should use the Information Stream & output that users using Find-Command/DSCResource/Module/RoleCapability/Script should move to using Find-PSResource & then remove these wrappers in a v4 release of PowerShellGet


With support of the generic `PSResource`, this means we can also find and
install arbitrary nupkgs that may only contain an assembly the user wants to
use in their script.

A `-Version` paramter will accept a string of a [Nuget version range syntax](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards).

A `-Prerelease` switch will allow include prerelease resources in the results.

A `-ModuleName` paramter will accept a string, and will return commands, DSC resources, and Role Capabilities found in modules with that name.

A `-Tags` parameter will accept a string array and will filter results based on PSResource tags.

A `-Proxy` parameter will accept a uri.

A `-ProxyCredential` parameter will accept a PSCredential

A `-Credential` paramter will accept a PSCredential.

An `-IncludeDependencies` switch will include dependent resources in the returned results.

`Find-Module` will be retained to provide compatibility with v2.

### Installing resources

`Install-PSResource` will mirror the parameter set for `Find-PSResource`.
A `-Repository` parameter accepts a specific repository name or URL to the repository:

```powershell
Install-PSResource myModule -Repository 'https://mygallery.com'
# or
Install-Module myModule -Repository 'https://mygallery.com'
```

If `-Repository` is not specified and the same resource is found in multiple
repositories, then the resource is automatically installed quietly from the
trusted repository with the highest version matching the `-Version` parameter
(if specified, otherwise newest non-prerelease version unless `-Prerelease`
is used).
If there are no trusted repositories matching the query, then the newest version
fulfilling the query will be prompted to be installed from the highest priority
repositories.
If there are multiple repositories with the same priority level containing the same
version, the first one registered is used and will be prompted to install.

`-TrustRepository` can be used to suppress being prompted for untrusted sources.
`-SkipPublisherCheck` can be used to suppress being prompted if the publisher
of the module is different from the currently installed version.
`-Reinstall` can be used to re-install a module.

A `-Type` parameter will accept an array of types to allow filtering.

A `-Version` parameter will accept a string of a [Nuget version range syntax](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards).

A `-Proxy` parameter will accept a uri.

A `-ProxyCredential` parameter will accept a PSCredential

A `-Credential` parameter will accept a PSCredential.

A `-NoClobber` switch will prevent installing modules that have the same cmdlets
as a differently named module already on the system.

A `-Quiet` switch will suppress progress information.

`-Scope` with `AllUsers` and `CurrentUser` will work the same as it does in v2.
Default is `CurrentUser`.

If aliases are enabled `Install-Module` will call to `Install-PSResource -Type Module`.

v3 will continue to use the versioning folder format as v2 in that it will be
Major.Minor.Patch and not contain the semver prerelease label.

Comment on lines +185 to +187

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I think should allow use of the pre-release label to enable side by side comparisons of pre-releases like we can for general releases, especially during the developing of these pre-release.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could add the pre-release label in 7.1 and continue to use the old format for everything previous. At least get support for the pre-release label in 7.1 so we have the option to adopt this later.

### Dependencies and version management

`Install-PSResource` can accept a path to a psd1 or json file (using `-RequiredResourceFile`),
or a hashtable or json (using `-RequiredResource`) where the key is the module name and the
value is either the required version specified using Nuget version range syntax or
a hash table where `repository` is set to the URL of the repository and
`version` contains the [Nuget version range syntax](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards).

```powershell
Install-PSResource -RequiredResource @{
'Configuration' = '[1.3.1,2.0)'
'Pester' = @{
version = '[4.4.2,4.7.0]'
repository = 'https://www.powershellgallery.com'
credential = $cred
allowPrerelease = $true
}
}
```
In this case the modules named "Configuration", and "Pester" will be installed.
The json format will be the same as if this hashtable is passed to `ConvertTo-Json`:

```json
{
'Configuration': '[1.3.1,2.0)',
'Pester': {
'version': '[4.4.2,4.7.0]',
'credential': null,
'repository': 'https://www.powershellgallery.com',
'prerelease': true
}
}
```

The older `System.Version` four part version type will be supported to retain
compatibility with existing published modules using that format.

Declared dependencies are searched and installed using the same trust algorithm
as described for `Install-PSResource` above.

### Saving resources

This cmdlet uses `Install-PSResource -DestinationPath`.

With the removal of PackageManagement, there is still a need to support saving
arbitrary nupkgs (assemblies) used for scripts.

A `-AsNupkg` switch will save the resource as a nupkg (if it was originally a
nupkg) instead of expanding it into a folder.

### Updating resources

`Update-PSResource` will update all resources to most recent version by default.

### Publishing resources

`Publish-PSResource` will supporting publishing modules, scripts and nupkgs.
It will follow the same model as `Publish-Module` in v2.

A `-DestinationPath` can be used to publish the resulting nupkg locally.
`Publish-PSResource` can accept a path to the nupkg to publish to a repository.

A `-SkipDependenciesCheck` switch can be used to bypass the default check that
all dependencies are present.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... all dependencies are present where? on the local system or in the target repository?


### Listing installed resources

`Get-PSResource` will list all installed resources with new `Type` column.

```output
Version Name Repository Description
------- ---- ---------- -----------
5.0.0 VSTeam PSGallery Adds functionality for working with Visual …
0.14.94 PSGitHub PSGallery This PowerShell module enables integration …
0.7.3 posh-git PSGallery Provides prompt with Git status summary …
1.0.1 PSAutoMute PSGallery Powershell script to Auto Mute you sound devices …
```

### Uninstalling resources

`Uninstall-PSResource` will be available to remove installed resources.

>[!NOTE]
>Uninstalling dependencies automatically will be something to consider in the future.

If a package is unable to be uninstalled because it is currently loaded, the cmdlet will emit a warning
which enumerates the packages that were unable to be installed.
We will explore a dependency clean parameter in a future version of PowerShellGet.

When uninstalling the dependency check will be performed not only on the RequiredModules.Name,
but also on RequiredModules.Version, which should be less-than-or-equal to the version of the module being
uninstalled in order to be considered a dependency.

### Proxy support

Each cmdlet will have `-Proxy` and `-ProxyCredential` parameters.

### Open Sourcing the module

In order to open source PowerShellGet 3.0 we will fork the current PowerShellGet repo, rename it as PowerShellGetv2,
and use the existing PowerShellGet repo as the primary support and development contact for PowerShellGet 3+.
Comment on lines +287 to +288

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I so wish this had not been conducted this way as it is a terrible user experience for current users of PowerShellGetv2 including those like me that had it cloned already

Forking and calling it PowerShellGetv3 would have been a much better option, but generally a v3 branch & the branches off of this within the original repository generally makes the most sense for development & should be adhered to in future splits of modules where possible.


## Alternate Proposals and Considerations

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we leave the door open for custom repository providers or other sources? I am really interested in using blob storage as a module repository source. I'm seeing more and more multi account cloud configurations where network connectivity to a central nuget server doesn't work out very well.


These are items are outside the scope of this RFC and version 3.0.
This list includes a number of features that were included in the inital versions
of the RFC but did not meet the final scope of PowerShellGet 3.0.
Many of these items can be addressed in a future version of PowerShellGet 3.x without
introducing a breaking change:

- This RFC does not cover the module authoring experience on publishing a cross-platform
module with multiple dependencies and supporting multiple runtimes.

- [Should be the opposite] If there is a desire to explicitly update the local cache (like `apt`), we can introduce a
`Update-PSResourceCache` cmdlet in the future and a property on PSRepository registrations
indicating whether it auto-updates or not.

- The ability to set policy for PSGet

- Automatic cleanup of old resources when upgrading or removing resources

- Signing incorporated into `Publish-PSResource`

- `-Trusted` switch to `Install-PSResource` and `Find-PSResource` that pre-validates
the signing cert is trusted on the system

- Notify users that they have outdated versions of a module (just patch versions?)

- A way to filter resources to those allowed to be installed on the system

- Enterprise management of local cache/the ability to configure a PSRepository on a system-wide level

- Full semver support won't happen until there is a semver type in .NET

- If the prerelease switch is not used, and only a prerelease version of the module is found a warning will be emitted.

- Using a merkle tree to validate modules

- Support for automatic searches from Find-PSResource -Type Command when a command in not found in module discovery
```powershell
PS> Get-Satisfaction
Get-Satisfaction : The term 'Get-Satisfaction' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Get-Satisfaction
+ ~~~
+ CategoryInfo : ObjectNotFound: (Get-Satisfaction:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Suggestion: You can obtain `Get-Satisfaction` by running `Install-PSResource HappyDays`.
PS> Install-PSResource HappyDays
```

- PowerShell module loading needs to be updated to [understand semver](https://github.com/PowerShell/PowerShell/issues/2983).

- Local cacheing,instead of always connecting to a repository to perform an online search,
`Find-PSResource` (see below) works against a local cache.
This will also enable changes in PowerShell to use `Find-PSResource -Type Command` to look
in this cache when it can't find a command and suggest to the user the module to install to
get that command.
This will be a local json file (one per repository) containing sufficient metadata
for searching for resources and their dependencies.
The cache will be stored in the user path.
There is no system cache that is shared.
A system wide cache would require elevation or sudo to create and update preventing
it from being useful.

Example cache entry:

```json
{
"name": "This is my module",
"exportedFunctions": [
"Get-One",
"Set-Two"
],
"version": "1.0.0.0",
"type": "module",
"tags": [
"Linux",
"PSEdition_Core",
"PSEdition_Desktop",
"AzureAutomation"
]
}
```

An example cache with 5000 resources (approximately the number of unique packages
currently published to PowerShellGallery.com) is ~700KB in compressed json form.

The cache would have both latest stable and latest prerelease versions of resources.

- Updating of the local cache

- Credentials management using [Secrets Management RFC](https://github.com/PowerShell/PowerShell-RFC/pull/208) for generalized credential management.
The `repository` property can be the name of the registered repository or the URL
to a repository.
If no `repository` is specified, it will use the `default` repository.

- `Save-PSResource -Type Library` will download nupkgs that have a `lib` folder.
The dependent native library in `runtimes` matching the current system runtime
will be copied to the root of the destination specified.
A `-IncludeAllRuntimes` can be used to explicitly retain the `runtimes` directory
hierarchy within the nupkg to the root of the destination.

- Upon failure to connect to PSGallery, the cmdlets should retrieve status from
a well known REST API and return a more descriptive error message to the user.