renovatebot / renovate

Home of the Renovate CLI: Cross-platform Dependency Automation by Mend.io
https://mend.io/renovate
GNU Affero General Public License v3.0
16.48k stars 2.11k forks source link

PowerShell Module/Script Dependency Support #15155

Open SphenicPaul opened 2 years ago

SphenicPaul commented 2 years ago

What would you like Renovate to be able to do?

Having found Renovate recently, and found it very useful for .NET, Docker, Terraform and Go dependency management, it doesn’t currently support the recognition and upgrades of PowerShell modules and scripts.

Example PowerShell scripts might include files with ‘.ps1’, ‘.psd1’ and ‘.psm1’ extensions, and typically would reference other PowerShell modules (and versions) by either: 1) The ‘#Requires -Module’ declaration/parameter as detailed here: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires?view=powershell-7.2#-modules-module-name--hashtable 2) A specific version of a required module defined within “RequiredModules” section of the ‘.psd1’ file (typically a PowerShell, module manifest), as defined under the ‘RequiredModules’ row in the following table: https://docs.microsoft.com/en-us/powershell/scripting/developer/module/how-to-write-a-powershell-module-manifest?view=powershell-7.2 3) References/Usages within scripts/files where the ‘Install-Module’ and/or ‘Import-Module’ functions/cmdlets are used with the ‘-RequiredVersion’/‘-MinimumVersion’/‘-MaximumVersion’ parameter passed/used (although I suspect the ‘-MaximumVersion’ has less relevance in this use case).

If you have any ideas on how this should be implemented, please tell us here.

PowerShell PackageManagement/PowerShellGet uses NuGet functionality behind-the-scenes (PowerShell repos are NuGet repos, and PowerShell module packages are NuGet packages) so there are potentially workarounds for this by setting up configurations to include additional files/patterns/repositories under the NuGet-specific configuration (I’ve not looked further into this yet, but if anyone has easy/simple config examples to point me in right direction, that would be helpful). The output of any work (interim or permanent) to meet this requirement in this way could just be some examples (for PowerShell dependencies by using NuGet) within the documentation.

In lieu of a NuGet-based workaround, another option might be to make PowerShell a first-class citizen of this (so it has it’s own configuration sections/objects/etc.) - If deemed valid/useful enough to be added, I’d also be intersect to know a suitable approach to take to getting this functionality added - I might try and find a little time to get something submitted.

Is this a feature you are interested in implementing yourself?

Maybe

viceice commented 2 years ago

a workaround, which should already work is using regex manager, use nugget datasource and assign PowerShell gallery as registry url .

github-actions[bot] commented 2 years ago

Hi there,

You're requesting support for a new package manager. We need to know some basic information about this package manager first. Please copy/paste the new package manager questionnaire, and fill it out in full.

Once the questionnaire is filled out we will evaluate if adding support for this manager is something we want to do.

Good luck,

The Renovate team

SphenicPaul commented 2 years ago

Thanks @viceice

I've managed to trawl through the documentation and (with more than a little trial and error) I've been able to get some suitable regexManagers configuration setup/defined that makes use of the public, PowerShell gallery (https://www.powershellgallery.com/) as a package/module source and caters for the 3 scenarios I'd highlighted in the original issue: 1) The #Requires -Module declaration/parameters in .ps1 (and .psm1) scripts 2) The RequiredModules section of the .psd1 (module manifest) file
3) Install-Module and/or Import-Module functions/cmdlet usages

Incase somebody might find this useful, the regexManagers config, with some examples of the patterns found are below (Note: The regular expressions could potentially be more concise and performant, but seems to functionality provide what I'd required).

Note that, in my case/scenario, I'm not going to be referencing the public, PowerShell gallery directly in my own config, but I've also been able to get this working within Azure DevOps, using a private, NuGet repository (referenced in the config) which is then using the pubic, PowerShell gallery as an "Upsteam Source". I've left in the commented-out lines below (replace the placeholders - yourorgnamehere and yournugetreponamehere, but you'd also need to setup the credentials for the internal feed elsewhere in your config.

Additionally, I've also not included MaximumVersion, PowerShell cmdlet/function parameters in my config/regex because my assumption is that if I'm making use of MaximumVersion then I don't want Renovate to auto-upgrade/amend these (although you should be able to amend the below regex relatively easily if you did want that functionality).

module.exports = {

  ...<snip>...

  regexManagers: [
    {
      "fileMatch": [
        "^.*\\.psd1", // PowerShell, 'Module Manifest' file(s)
      ],
      "matchStrings": [

        // 'RequiredModules' usages (Single and multi-line) - Examples:
        //   RequiredModules={@{ModuleName='SomeModuleName'; ModuleVersion="0.10.0"}}
        //   @{ModuleName='SomeOtherModuleName'; ModuleVersion="0.10.0"}
        //   @{ModuleName='YetAnother.ModuleName'; ModuleVersion='0.10.0'; Guid='a622dea5-2a7a-4616-a270-1f7abb777e71'}
        "(?i)\\bModuleName\\b\\s*=\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"]\\s*\\;?\\s*(?i)\\bModuleVersion\\b\\s*=\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

        ],
      "datasourceTemplate": "nuget", // NuGet is used by PowerShell(Get)/PackageManagment
      "registryUrlTemplate": "https://www.powershellgallery.com/api/v2/",
      //"registryUrlTemplate": "https://yourorgnamehere.pkgs.visualstudio.com/_packaging/yournugetreponamehere/nuget/v3/index.json",
    },
    {
      "fileMatch": [
        "^.*\\.ps1",  // PowerShell, 'Script' file(s)
        "^.*\\.psm1", // PowerShell, 'Module Script' file(s)
      ],
      "matchStrings": [

        // '#Requires' usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
        //   #Requires -Modules @{ ModuleName = "SomeModuleName" ; RequiredVersion = '0.1.0' }
        //   #Requires -Modules @{ ModuleName="SomeOtherModuleName"; ModuleVersion="1.2.3" }
        "(?i)#\\bRequires\\b.*\\bModules\\b.*\\bModuleName\\b\\s*=\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"]\\s*\\;?\\s*(\\bRequiredVersion\\b|\\bModuleVersion\\b)\\s*=\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

        // "Install-Module" and "Import-Module" (named-parameter) usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
        //   Install-Module  -name 'SomeModuleName' -MinimumVersion '4.2.1'
        //   Import-Module  -Name 'SomeOtherModuleName' -MinimumVersion '1.2.3'
        //   Install-Module -name 'YetAnother.ModuleName' -SomeOtherParameter 'ABC' -AnotherParameter "xyz" -RequiredVersion '2.3.1'
        //   Import-Module -name "One.Final.Module.Name" -SomeOtherParameter 'abc' -RequiredVersion "1.2.4"
        "(?i)(\\bImport\\b|\\bInstall\\b)-Module\\s*\\-\\bName\\b\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"].*\\-(\\bRequiredVersion\\b|\\bMinimumVersion\\b)\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

        // "Install-Module" and "Import-Module" (parameter-position-zero) usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
        //   Install-Module 'SomeModuleName' -SomeOtherParameter 'XYZ' -RequiredVersion '0.1.2'
        //   Import-Module "SomeOtherModuleName" -SomeOtherParameter 'xyz'  -RequiredVersion "3.1.0"
        //   Install-Module 'YetAnother.ModuleName' -SomeOtherParameter 'ABC' -AnotherParameter "xyz" -MinimumVersion '0.1.2'
        //   Import-Module  "One.Final.Module.Name" -SomeOtherParameter 'abc'  -MinimumVersion "0.1.3"
        "(?i)(\\bImport\\b|\\bInstall\\b)-Module\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"].*\\-(\\bRequiredVersion\\b|\\bMinimumVersion\\b)\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

        ],
      "datasourceTemplate": "nuget", // NuGet is used by PowerShell(Get)/PackageManagment
      "registryUrlTemplate": "https://www.powershellgallery.com/api/v2/",
      //"registryUrlTemplate": "https://yourorgnamehere.pkgs.visualstudio.com/_packaging/yournugetreponamehere/nuget/v3/index.json",
    },
  ],

  ...<snip>...
};
SphenicPaul commented 2 years ago

Also, while this does fulfil my short-term requirements, I'll complete the `New package manager questionnaire' and add to this thread - Whether it wants/needs to be a new package manager, can be decided upon at a later point.

SphenicPaul commented 2 years ago

New package manager questionnaire

Did you read our documentation on adding a package manager?

Basics

Name of package manager

PowerShellGet / PackageManagement

What language does this support?

PowerShell (specifically for PowerShell 'Modules') including PowerShell Desired State Configuration (DSC) modules/components.

How popular is this package manager?

PowerShell Gallery metrics (as of 19/04/2022):

Does this language have other (competing?) package managers?


Package File Detection

What type of package files and names does it use?

PowerShellGet / PackageManagement used within PowerShell is based upon NuGet package management. Installed packages (typically, PowerShell modules)

What fileMatch pattern(s) should be used?

Is it likely that many users would need to extend this pattern for custom file names?

Is the fileMatch pattern likely to get many "false hits" for files that have nothing to do with package management?

Feasibly, the above would recognise all .ps1, PowerShell scripts in a repository, for example, but any one, or all of those scripts may contain the functionality for finding, installing and importing modules (Find-Module, Install-Module and Import-Module, respectively). The .psm1 files would be similar to the .ps1 script, but there might be less likelyhood of similar functionality being included/needed in that file type.

In the case of the .psd1 file - There may be other .psd1 files used (it is a PowerShell data file, afterall), but in most cases, these will be much less common (compared to .ps1 files/scripts), and the structure of the content of the .psd1 file will likely highlight this as a PowerShell module manifest (which holds the module dependencies and versions, where specified).


Parsing and Extraction

Can package files have "local" links to each other that need to be resolved?

No.

Is there a reason why package files need to be parsed together (in serial) instead of independently?

No.

What format/syntax is the package file in?

How do you suggest parsing the file?

Does the package file structure distinguish between different "types" of dependencies? e.g. production dependencies, dev dependencies, etc?

List all the sources/syntaxes of dependencies that can be extracted

.psd1 file module examples and regex :

// 'RequiredModules' usages (Single and multi-line) - Examples:
//   RequiredModules={@{ModuleName='SomeModuleName'; ModuleVersion="0.10.0"}}
//   @{ModuleName='SomeOtherModuleName'; ModuleVersion="0.10.0"}
//   @{ModuleName='YetAnother.ModuleName'; ModuleVersion='0.10.0'; Guid='a622dea5-2a7a-4616-a270-1f7abb777e71'}
"(?i)\\bModuleName\\b\\s*=\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"]\\s*\\;?\\s*(?i)\\bModuleVersion\\b\\s*=\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

.ps1 and .psm1 file module examples and regex :

// '#Requires' usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
//   #Requires -Modules @{ ModuleName = "SomeModuleName" ; RequiredVersion = '0.1.0' }
//   #Requires -Modules @{ ModuleName="SomeOtherModuleName"; ModuleVersion="1.2.3" }
"(?i)#\\bRequires\\b.*\\bModules\\b.*\\bModuleName\\b\\s*=\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"]\\s*\\;?\\s*(\\bRequiredVersion\\b|\\bModuleVersion\\b)\\s*=\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

// "Install-Module" and "Import-Module" (named-parameter) usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
//   Install-Module  -name 'SomeModuleName' -MinimumVersion '4.2.1'
//   Import-Module  -Name 'SomeOtherModuleName' -MinimumVersion '1.2.3'
//   Install-Module -name 'YetAnother.ModuleName' -SomeOtherParameter 'ABC' -AnotherParameter "xyz" -RequiredVersion '2.3.1'
//   Import-Module -name "One.Final.Module.Name" -SomeOtherParameter 'abc' -RequiredVersion "1.2.4"
"(?i)(\\bImport\\b|\\bInstall\\b)-Module\\s*\\-\\bName\\b\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"].*\\-(\\bRequiredVersion\\b|\\bMinimumVersion\\b)\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

// "Install-Module" and "Import-Module" (parameter-position-zero) usages (Note: Excludes 'MaximumVersion' parameter) - Examples:
//   Install-Module 'SomeModuleName' -SomeOtherParameter 'XYZ' -RequiredVersion '0.1.2'
//   Import-Module "SomeOtherModuleName" -SomeOtherParameter 'xyz'  -RequiredVersion "3.1.0"
//   Install-Module 'YetAnother.ModuleName' -SomeOtherParameter 'ABC' -AnotherParameter "xyz" -MinimumVersion '0.1.2'
//   Import-Module  "One.Final.Module.Name" -SomeOtherParameter 'abc'  -MinimumVersion "0.1.3"
"(?i)(\\bImport\\b|\\bInstall\\b)-Module\\s*[\\'\\\"](?<depName>.*?)[\\'\\\"].*\\-(\\bRequiredVersion\\b|\\bMinimumVersion\\b)\\s*[\\'\\\"](?<currentValue>.*?)[\\'\\\"]",

Also note, there are maybe options to pipe the Find-Module output into Install-Module, or similar like so

Find-Module 'SomeModuleName' -RequiredVersion '1.2.3' | Install-Module

.. but Find-Module isn't catered for in the above examples (which are taken from my earlier post in this thread).

Describe which types of dependencies above are supported and which will be implemented in future

I don't believe any of the above dependencies are supported within Renovate as it stands, although I have been able to facilitate a similar requirement by making use of a RegexManager definition (example in earlier post).


Versioning

What versioning scheme does the package file(s) use?

Typically, it's 3 or 4 part, versioning (e.g. 1.2.3 or 1.2.3.4), athough, I believe 1 and two part versions are acceptable, and are treated like 3 part versions (with 0 for more granular, versioning components) - e.g. Version 1.0 would be effectively 1.0.0.

Does this versioning scheme support range constraints, e.g. ^1.0.0 or 1.x?

In the .ps1 scripts that call functions/cmdlets you are providing a single version with the parameter defining how the version provided is used. For example: -ModuleVersion '1.22.3' - Any module version greater or equal to 1.22.3 -RequiredVersion '1.22.3' - Exact version of 1.22.3 -MinimumVersion '1.22.3' - Any module version greater or equal to 1.22.3 -MaximumVersion '1.22.3' - Any module version less than or equal to 1.22.3

In the .psd files (module manifests), typically a single version is being specified, which I believe still means that it requires any module version greater or equal to the version specified.

Is this package manager used for applications, libraries, or both? If both, is there a way to tell which is which?

If ranges are supported, are there any cases when Renovate should pin ranges to exact versions if rangeStrategy=auto?

n/a


Lookup

Is a new datasource required? Provide details

Technically, this is just using NuGet so might be simlar setup to that. There is a public [PowershellGallery]() which complies with NuGet v2 functionality. Package/API URI is:

Will users need the capability to specify a custom host/registry to look up? Can it be found within the package files, or within other files inside the repository, or would it require Renovate configuration?

Users can register their own package repositories onto a server/workstation using Register-PSRepository which will allow them to find/install packages from those locations (implicitly, because they are registered on their server/workstation, or by explicitly, providing the repository name to only search/use that repository). Note that users can provide their own names and URIs for repositories and it's unlikely they will be consistent from user to user, although note that the PSGallery repository (using the PowerShellGallery API URI above) is typically registered on Windows machines that have PackageManagement installed - I'd argue that would be a typically repository that most would have registered.

Any other user-specific/defined repositories are likely to be NuGet repositories setup within a third-party tool (e.g. Nexus Repository / Artifactory / etc.) or a file/network share. It's likely that users would have to configure Renovate to use their own custom registry URIs (as they do with NuGet, at present).

Do the package files have any "constraints" on the parent language (e.g. supports only v3.x of Python) or platform (Linux, Windows, etc) that should be used in the lookup procedure?

The PowerShell modules do define which version of PowerShell they are built for (primarily for preventing install of modules built for newer versions of PowerShell being able to be installed within older versions of PowerShell). I've not seen a scenario, to date, where this would impact dependency lookups/constraints - For example, you could have a PowerShell module that supports PowerShell v5.1 that references another module that supports back to PowerShell v3.

Will users need the ability to configure language or other constraints using Renovate config?

Not initially sure on this point.


Artifacts

Are lock files or checksum files used? Are they mandatory?

These might be used behind the scenes but they don't seem to be visible in day-to-day usage. Note that most modules do have a GUID defined within the .psd1 that is meant to uniquely identify that module (although in practice, people can generate/use their own GUID so there may be clashes across some less-known used/modules, perhaps).

If so, what tool and exact commands should be used if updating one or more package versions in a dependency file?

n/a

If applicable, describe how the tool maintains a cache and if it can be controlled via CLI or env? Do you recommend the cache be kept or disabled/ignored?

n/a

If applicable, what command should be used to generate a lock file from scratch if you already have a package file? This will be used for "lock file maintenance"

n/a

Other

Is there anything else to know about this package manager?

As previously mentioned, PowerShell PackageManagement/PowerShellGet seems to make use of NuGet packages for storing/versioning modules so I'd consider this similar to NuGet 'under the hood' but with scripts (.ps1 files) and module manifests/datafiles (.psd1 files) using and defining those modules and their dependencies.

As also mention and highlighted/exemplified in an earlier post, this can be implemented using the RegexManager - That might be suitable-enough and save an additional, package manager implementation.

Feel free to shout/tag me if you've any further questions etc.

viceice commented 2 years ago

i played with those modules already as part of building CI server / dev machines. so i can say we can reuse nuget versioning and datasource. we just need to use a different registry url for Powershell Gallery by default.