chocolatey / choco

Chocolatey - the package manager for Windows
https://chocolatey.org
Other
10.28k stars 902 forks source link

Add option to include install-args in the `choco export` command #2503

Open jimbrig opened 2 years ago

jimbrig commented 2 years ago

It would be nice to have a flag similar to the --include-version-numbers flag for the choco export command but for including the installation arguments of a given package (assuming the user has the configuration useRememberedArgumentsForUpgrades enabled).

Consider the following scenario where I install Git with various installation arguments applied:

cinst -y git.install --installargs "'/COMPONENTS=icons,assoc,assoc_sh,autoupdate,windowsterminal,scalar'" --params "/GitAndUnixToolsOnPath /NoAutoCrlf /WindowsTerminal /NoShellIntegration /NoGuiHereIntegration /NoShellHereIntegration /WindowsTerminalProfile /DefaultBranchName:main /Editor:VisualStudioCode"

then run choco export -o="choco-packages.config", however the output XML only contains:

<package id="git.install"/>

whereas it could contain something like so:

<package id="git.install" arguments=" --install-arguments=&quot;&apos;/COMPONENTS=icons,assoc,assoc_sh,autoupdate,windowsterminal,scalar&apos;&quot; --package-parameters=&quot;&apos;/GitAndUnixToolsOnPath /NoAutoCrlf /WindowsTerminal /NoShellIntegration /NoGuiHereIntegration /NoShellHereIntegration /WindowsTerminalProfile /DefaultBranchName:main /Editor:VisualStudioCode&apos;&quot;" />

This way, when I run cinst -y choco-packages.config it would apply the initial install-args and params for Git.

Proposed Solution: add --include-install-args flag to the choco export command.

jimbrig commented 2 years ago

I investigated a little further and found that installation arguments are stored under the path: $env:ChocolateyInstall\.chocolatey\<package>.<version>\.arguments (again, assuming the user has the config option useRememberedArgumentsForUpgrades enabled).

Therefore, to implement the proposed enhancement above to the export command, you could gather package specific installation arguments using some helper functions like these:

Add-Type -AssemblyName System.Security
$entropyBytes = [System.Text.UTF8Encoding]::UTF8.GetBytes("Chocolatey")

Function Unprotect-Arguments {
    param([string]$data)
    $encryptedByteArray = [System.Convert]::FromBase64String($data)
    $decryptedByteArray = [System.Security.Cryptography.ProtectedData]::Unprotect(
        $encryptedByteArray,
        $entropyBytes,
        [System.Security.Cryptography.DataProtectionScope]::LocalMachine
    )
    return [System.Text.UTF8Encoding]::UTF8.GetString($decryptedByteArray)
}

Function Read-Arguments {
    param([string]$packageName)
    $directory = Get-ChildItem $env:ChocolateyInstall\.chocolatey -Directory -Filter "$packageName*" | `
        Where-Object { $_.Name -match ("$packageName" + "\.[\d\.]+") } | Select-Object -Last 1
    If (!($directory)) { return }
    $argsFile = Join-Path $directory.fullname ".arguments"
    If (Test-Path $argsFile) {
        $argsData = Get-Content $argsFile
        #Implicitly return result from Unprotect-Arguments
        Unprotect-Arguments -data $argsData
    }
}

Function Get-LocalPackageInfo {
    & choco list -lo -r -y
}

Function Get-PackagesConfigBody($SaveArguments = $false) {
    Get-LocalPackageInfo | ForEach-Object {
        $packageName = $($_.SubString(0, $_.IndexOf("|")))
        $line = '   <package id="' + $packageName + '" '

        If ($SaveArguments) {
            $line = $line + 'arguments="' + [System.Security.SecurityElement]::Escape($(Read-Arguments $packageName)) + '" '
        }

        $line = $line + '/>'
        $line

    }
}

Function Write-PackagesConfig($OutputFile, $SaveArguments = $false) {
    $header = "<?xml version=`"1.0`" encoding=`"utf-8`"?>`n<packages>"
    $footer = "</packages>"
    $body = Get-PackagesConfigBody -SaveArguments $SaveArguments
    Write-Output $header $body $footer | Out-File $OutputFile -Encoding ASCII
}

and choco export -o="choco-packages.config" --include-install-args would utilize something like:

Write-PackagesConfig -OutputFile "choco-packages.config" -SaveArguments $true

Resulting in XML like this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
   <package id="gh" arguments=" --cache-location=&quot;&apos;C:\Users\jimmy\AppData\Local\Temp\chocolatey&apos;&quot;" />
   <package id="git.install" arguments=" --cache-location=&quot;&apos;C:\Users\jimmy\AppData\Local\Temp\chocolatey&apos;&quot; --install-arguments=&quot;&apos;/COMPONENTS=icons,assoc,assoc_sh,autoupdate,windowsterminal,scalar&apos;&quot; --package-parameters=&quot;&apos;/GitAndUnixToolsOnPath /NoAutoCrlf /WindowsTerminal /NoShellIntegration /NoGuiHereIntegration /NoShellHereIntegration /WindowsTerminalProfile /DefaultBranchName:main /Editor:VisualStudioCode&apos;&quot;" />
</packages>

I tested this suite of functions locally and it worked exactly as mentioned above. Only potentially undesired aspect of the result is the --cache-location being included from the reading of the encrypted .chocolatey/<package>.<version>/.arguments file.

Here's the script I used (as .txt due to GitHub limitations):

TheCakeIsNaOH commented 2 years ago

Thanks for adding this issue, I had thought that there already was one posted for this feature request.

This is definitely something that is planned to be added at some point, but it would be in the C# code, not in the PowerShell helper functions. See #1825 for where it would go in.

Also, I discussed this with @gep13, and it is likely that the other install argument would be added with individual elements, instead of via a single arguments element. This would require that #886 be completed first, and makes changes so the code for parsing CLI arguments can be reused for saved arguments.

Cyber1000 commented 2 years ago

Will this add the state of pinned-packages like stated here? https://github.com/chocolatey/choco/pull/1825#issuecomment-491901221

Exactly. So if I pinned VS Code on one machine, it would end up pinned on another machine.

Wasn't quite sure if I should open a new issue, but would find it quite useful to export this with choco export

TheCakeIsNaOH commented 2 years ago

Wasn't quite sure if I should open a new issue,

@cyber1000 In this case, a new issue would be appropriate. The information about whether a package is pinned or not is stored separately from the saved arguments, and there is not currently a way to pin a package during installation (although I do have a PR open to add that).

bn-c commented 5 months ago

👀 Any ETA on this feature getting merged?

pauby commented 5 months ago

We hope to be bringing this in shortly.

gep13 commented 5 months ago

Based on the changes in the associated PR's for this issue, I am going to have to bump this issue from the 2.3.0 release, and instead look at this again in the 2.4.0.

The suggested changes to the INuGetService mean that this would essentially be a breaking change, that would need a corresponding change in the Chocolatey Licensed Extension, which we haven't planned to do at the minute.

Instead of holding up the release of 2.3.0, we will look to do the work in this issue in a later release.

Apologies for any inconvenience that this causes.