Skip to content
Open
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
150 changes: 150 additions & 0 deletions ValidateNuGetPackages.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<#
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this script? I think we are running dotnet pack as part of PR build (or we should) so better way might be changing its severity (simular to what ADO does) to fail on missing data or other errors. Also look into https://devblogs.microsoft.com/dotnet/package-validation/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The publishing to NuGet.org fails if some attributes are missing, e.g. projecturl

I took a look at the package validation link, but I could not find a way to validate if a particular attribute is present in the nuspec file.

I agree that having this validation in the pack step is the preferred way. Do you know how we could use the pack validation to achieve this?

Copy link
Contributor

@AndreyTretyak AndreyTretyak Oct 15, 2021

Choose a reason for hiding this comment

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

I assume it's some kind of parameter that ADO pass to dotnet pack task, but github does not, so we can just compare logs.
Also if we need some custom validation I think having MSBuild target in Directories.Build.targets would be much simpler and it would run automatically.
For example:

<Target Name="ValidateNugetPackage" BeforeTargets="pack" Condition="'$(IsPackable)' == 'true'">
    <Error Condition="'$(Tags)' == ''" Text="Tags should not be empty" />
    ...
<Target>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially I tried to add these conditions to Directories.Build.targets but it didn't work because these attributes were always evaluated as empty - for some reason it did not take into account attributes from csproj files.


.SYNOPSIS

This script validates NuGet packages for required attributes.

.EXAMPLE

Microsoft.Omex.Tools.ValidateNuGetPackages.ps1 c:\omex\nuget

.DESCRIPTION

There are several required attributes in NuGet specification files (nuspec) which are required by Microsoft
when the NuGet packages are published to NuGet.org.

If these attributes are missing, the packages cannot be published.

.NOTES

The script is intended to be used as a step in the pull request validation pipeline.
The script exits with code 1 if the validation failed.

The script verifies if the following attributes are present in the nuspec file:
- title
- description
- project url
- tags


.PARAMETER NuGetFolder
Path which contains the NuGet packages to be scanned.
The path is scanned recursively for *.nupkg files.

#>


param (
[Parameter(Mandatory = $true)]
[string]$NuGetFolder
)

<#
The validates the NuGet package for the required attributes
and returns false if some of them is missing.
#>
function ValidateNuGetPackage([string]$nugetPackage) {
Write-Host "Validating $nugetPackage"

$contents = ExtractNuSpec($nugetPackage)
$xmldoc = [xml]$contents

$result = $True

if ($xmldoc.package.metadata.title.Length -eq 0)
{
Write-Error "Title is missing."
$result = $False
}

if ($xmldoc.package.metadata.description.Length -eq 0)
{
Write-Error "Description is missing."
$result = $False
}

if ($xmldoc.package.metadata.projecturl.Length -eq 0)
{
Write-Error "ProjectUrl is missing."
$result = $False
}

if ($xmldoc.package.metadata.tags.Length -eq 0)
{
Write-Error "Tags is missing."
$result = $False
}

if ($result -eq $True)
{
Write-Host "Successfully validated NuGet package $nugetPackage"
}
else
{
Write-Error "NuGet package validation failed for $nugetPackage"
}

return $result
}

<#
The function extracts the contents of the NuGet specification file
from the NuGet archive.
#>
function ExtractNuSpec([string]$nugetPackage)
{
$zip = [io.compression.zipfile]::OpenRead($nugetPackage)
$file = $null
foreach ($entry in $zip.Entries)
{
if ($entry.FullName.EndsWith('.nuspec'))
{
$file = $entry
break
}
}

if ($null -eq $file)
{
Write-Error "Unable to find NuGet specification in NuGet package $nugetPackage"
return $false
}

$stream = $file.Open()
$reader = New-Object IO.StreamReader($stream)
$text = $reader.ReadToEnd()

$reader.Close()
$stream.Close()
$zip.Dispose()

return $text
}

Add-Type -assembly "system.io.compression.filesystem"

Write-Host "Scanning folder $NuGetFolder"

#Scanning the input folder recursively to get a list of NuGet packages to validate
$nugetFiles = Get-ChildItem "$NuGetFolder\\*.nupkg" -Recurse

Write-Host "The following $($nugetFiles.Count) NuGet packages will be validated"
foreach ($file in $nugetFiles)
{
Write-Host $file
}

$result = $true
foreach ($file in $nugetFiles)
{
$currentResult = ValidateNuGetPackage $file
if ($currentResult -eq $false)
{
$result = $false
}
}

if ($result -eq $false)
{
exit 1
}