Skip to content

Instantly share code, notes, and snippets.

@JPRuskin
Created June 20, 2024 10:33
Show Gist options
  • Save JPRuskin/a1da7ffb3178697185d551eb0283a5b4 to your computer and use it in GitHub Desktop.
Save JPRuskin/a1da7ffb3178697185d551eb0283a5b4 to your computer and use it in GitHub Desktop.
A function that downloads and internalizes Chocolatey packages, with functionality to remove dependencies you are absolutely sure you don't need.
# This assumes you have Chocolatey and Chocolatey.Extension installed and licensed correctly.
if (-not ('Nuget.Versioning.VersionRange' -as [type])) {
try {
Add-Type -AssemblyName $env:ChocolateyInstall\choco.exe
} catch {
$null = [System.Reflection.Assembly]::Loadfrom("$env:ChocolateyInstall\choco.exe")
}
}
function Get-InternalizedPackage {
<#
.Synopsis
Downloads and internalizes Chocolatey packages.
.Example
Get-InternalizedPackage cloudflared
# Returns a 'cloudflared' package with all binaries internalized.
.Example
Get-InternalizedPackage cloudflared -Source ChocolateyInternal
# Returns a 'cloudflare' package with all binaries internalized, from a specific source.
.Example
"dotnet-6.0-runtime", "dotnet-6.0-aspnetruntime", "dotnet-aspnetcoremodule-v2" | Get-InternalizedPackage -RemoveDependency KB2919355, KB219442, KB3033929, KB2999226 | Sort -Unique
# Returns all of the listed packages, and their dependencies, having removed some listed dependencies from the chain.
.Notes
This function is written far more trustingly than Chocolatey - e.g. it will not halt if it finds existing files.
#>
[OutputType([System.IO.FileInfo])]
[CmdletBinding()]
param(
# The package to download.
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string]$Id,
# The version to download.
[Parameter(ValueFromPipelineByPropertyName)]
[Nuget.Versioning.VersionRange]$Version,
# The source to download the package from.
[string]$Source,
# If added, removes the listed dependencies from the package before repacking it.
# WARNING: This can obviously create packages that don't work. Be careful.
[string[]]$RemoveDependency
)
begin {
$WorkingDirectory = Join-Path $env:TEMP "chocointernalize"
if (-not (Test-Path $WorkingDirectory)) {$null = mkdir $WorkingDirectory}
$SharedParameters = @{}
$PSBoundParameters.GetEnumerator().ForEach{
if ($_.Key -in @("RemoveDependency", "Source")) {
$SharedParameters += @{$_.Key = $_.Value}
}
}
}
process {
$PackageChanged = $false
# Download or find the package files
if (Test-Path "$WorkingDirectory\download\$Id") {
Write-Verbose "Using previously downloaded files in '$WorkingDirectory\download\$Id'"
} else {
Write-Verbose "Downloading package '$($Id)'"
$DownloadResult = choco @(
"download", $Id
if ($Version) {
$FoundVersion = if ($Version.OriginalString -as [NuGet.Versioning.SemanticVersion]) {
$Version.OriginalString
} else {
Write-Verbose "Finding Available Versions of '$($Id)'"
choco @(
"find"
$Id
"--exact"
"--all-versions"
if ($Source) {"--source=$Source"}
"--limit-output"
) | ConvertFrom-Csv -Delimiter '|' -Header Id, Version | Where-Object {
$Version.Satisfies($_.Version)
#} | Sort-Object -Descending {
## It may be necessary to sort, if we can't rely on the order returned
# [NuGet.Versioning.SemanticVersion]$_.Version
} | Select-Object -First 1 -ExpandProperty Version
}
Write-Verbose "Using Version '$($FoundVersion)'"
"--version=$FoundVersion"
}
if ($Source) {"--source=$Source"}
"--internalize"
"--internalize-all-urls"
"--ignore-dependencies"
"--output-directory=$($WorkingDirectory)"
"--no-progress"
)
if ($LastExitCode -ne 0) {
$DownloadResult
throw
}
}
# Get the metadata for the downloaded package
$NuspecPath = "$WorkingDirectory\download\$Id\$Id.nuspec"
[xml]$Nuspec = Get-Content $NuspecPath
$Namespace = [Xml.XmlNamespaceManager]::new($Nuspec.NameTable)
$Namespace.AddNamespace("nuspec", $Nuspec.package.xmlns)
# Check for dependencies we want to exclude
foreach ($UnwantedDependency in $RemoveDependency) {
if ($FoundDependency = $Nuspec.SelectSingleNode("//nuspec:dependency[@id='$($UnwantedDependency)']", $Namespace)) {
$PackageChanged = $true
Write-Verbose "Removing dependency '$($UnwantedDependency)' from '$($Id)'"
$null = $Nuspec.SelectSingleNode("//nuspec:dependencies", $Namespace).RemoveChild($FoundDependency)
}
}
# Save any modifications made
if ($PackageChanged) {
$Nuspec.Save($NuspecPath)
Write-Verbose "Repacking '$($Id)'"
$PackingResult = choco @(
"pack"
$NuspecPath
"--output-directory=$WorkingDirectory"
"--limit-output"
)
if ($LastExitCode -ne 0) {
$PackingResult
throw
}
} else {
Write-Verbose "There were no changes made to '$($Id)'"
}
# Return the package
Get-Item "$WorkingDirectory\$Id.$($Nuspec.package.metadata.version).nupkg"
# Recursively download any remaining dependencies
foreach ($Dependency in $Nuspec.SelectNodes("//nuspec:dependency", $Namespace)) {
Get-InternalizedPackage -Id $Dependency.Id -Version $Dependency.Version @SharedParameters
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment