MaxBeauchemin
1/15/2025 - 7:29 PM

Azure Devops - Nuget Restrictions

steps:
- task: PowerShell@2
  displayName: Check for Restricted Nuget Packages
  continueOnError: false
  inputs:
    pwsh: true
    targetType: inline
    script: |
      $restrictedPackages = @(
        @{
          PackageName = 'FluentAssertions'
          MaxVersion = '8.0.0'
          MaxInclusive = $false
          MinVersion = $null
          MinInclusive = $true
        }
      )

      $ErrorActionPreference = 'Stop'

      function Compare-Version {
        param(
          [string]$Version1,
          [string]$Version2
        )

        $v1 = $Version1.Split('.')
        $v2 = $Version2.Split('.')

        for ($i = 0; $i -lt [Math]::Max($v1.Count, $v2.Count); $i++) {
          $num1 = [int]($v1[$i] ?? 0)
          $num2 = [int]($v2[$i] ?? 0)

          if ($num1 -gt $num2) { return 1 }
          if ($num1 -lt $num2) { return -1 }
        }

        return 0
      }

      $packageRegex = [regex]'\<PackageReference\s+Include="([a-zA-Z.]+)"\s+Version="([0-9.]+)"\s*\/\>'
      $legacyPackageRegex = [regex]'\<Reference\s+Include="([a-zA-Z.]+),\s+Version=([0-9.]+).*?">'

      $path = ''

      $csprojFiles = Get-ChildItem -File -Recurse -Path $path -Filter '*.csproj'

      if ($csProjFiles.Count -gt 0)
      {
        Write-Output 'Scanning the following projects'
        Write-Output ''

        foreach ($file in $csProjFiles)
        {
          Write-Output "   > $($file.Name)"
        }

        Write-Output ''
      }
      else
      {
        Write-Output 'No projects found to scan'
        Write-Output ''
      }

      [System.Collections.ArrayList]$invalidReferences = @()
      [System.Collections.ArrayList]$validReferences = @()

      foreach ($file in $csprojFiles)
      {
        $fileText = Get-Content -Path $file.FullName

        [System.Collections.ArrayList]$allPackageMatches = @()

        $packageMatches = $packageRegex.Matches($fileText)
        $legacyPackageMatches = $legacyPackageRegex.Matches($fileText)

        if ($packageMatches.Count -gt 0)
        {
            $added = $allPackageMatches.AddRange($packageMatches)
        }
        if ($legacyPackageMatches.Count -gt 0)
        {
            $added = $allPackageMatches.AddRange($legacyPackageMatches)
        }

        foreach ($match in $allPackageMatches)
        {
          $name = $match.Groups[1].Value
          $version = $match.Groups[2].Value

          foreach ($package in $restrictedPackages)
          {
            if ($name -ieq $package.PackageName)
            {
              $invalid = $false

              if ($null -eq $package.MinVersion -and $null -eq $package.MaxVersion)
              {
                $invalid = $true
              }
              if ($null -ne $package.MinVersion)
              {
                $comparison = Compare-Version -Version1 $version -Version2 $package.MaxVersion

                if ($package.MinInclusive)
                {
                  if ($comparison -lt 0)
                  {
                    $invalid = $true
                  }
                }
                else
                {
                  if ($comparison -le 0)
                  {
                    $invalid = $true
                  }
                }
              }
              if ($null -ne $package.MaxVersion)
              {
                $comparison = Compare-Version -Version1 $version -Version2 $package.MaxVersion

                if ($package.MaxInclusive)
                {
                  if ($comparison -gt 0)
                  {
                    $invalid = $true
                  }
                }
                else
                {
                  if ($comparison -ge 0)
                  {
                    $invalid = $true
                  }
                }
              }

              $packageRef = "$($file.Name) : $name version $version"

              if ($invalid)
              {
                $added = $invalidReferences.Add($packageRef)
              }
              else
              {
                $added = $validReferences.Add($packageRef)
              }
            }
          }
        }
      }

      if ($validReferences.Count -gt 0)
      {
        Write-Output 'The following Nuget Packages have version restrictions, but the selected version is in compliance'
        Write-Output ''

        foreach ($ref in $validReferences)
        {
          Write-Output "   > $ref"
        }

        Write-Output ''
      }

      if ($invalidReferences.Count -gt 0)
      {
        Write-Warning 'At least 1 Nuget Package is restricted and cannot be used in its current form'
        Write-Output ''

        foreach ($ref in $invalidReferences)
        {
          Write-Output "   > $ref"
        }

        exit 1
      }
      else
      {
        Write-Output '🌈 Looks Good'
      }