Aktuelle NuGet Packages in Build Skripten verwenden

Alex Groß von GROSSWEBER hat uns freundlicherweise eine Ruby Klasse geschrieben, welche es ermöglicht im Build Vorgang immer die aktuellste Version eines NuGet Package heranzuziehen.

Wer beispielsweise MSpec als UnitTesting Framework einsetzt und ein entsprechendes Skript in seinem Build Server verwendet, der hatte das Problem, dass das Skript angepasst werden musste, sobald man MSpec per NuGet aktualisierte. Das gilt natürlich für jegliches Package, auf welches in Skripten direkt referenziert wird.

Um die Problematik nun elegant zu lösen, sei exemplarisch unten folgendes Szenario aufgelistet:

 

Hier zeige ich den Ruby Code, welcher bei uns im Build Skript zur Ausführung der Unit Tests verwendet wird. In Zeile 3 wird die Lösung von Alex verwendet. Erster Parameter gibt den Namen des Package an, während zweiter Parameter besagt wo alle Packages im Repository liegen. Als Rückgabewert erhält man den Pfad zur aktuellsten Version. Im nächsten Schritt in Zeile 4 iteriere ich über unser Binaries Verzeichnis drüber. Dabei werden alle DLLs verarbeitet, die mit ‘comWORK’ beginnen. Als Ausnahme gebe ich alle Dateien an, die auf ‘resources’ enden. Das Ergebnis ist eine Liste von DLLs, welche MSpec dann prüft bzw. ausführt. Die Reports landen im Verzeichnis ‘Reports’ und liegen in Form von html Dateien zur Ansicht bereit.

   1: desc 'Unit Tests'

   2: task :unit do

   3:     mspec = NuGetLatest('Machine.Specifications', 'Source\_Solutions\packages')

   4:     FileList.new('Binaries/**/comWORK.*.dll').exclude('**/*.resources.dll').each do |f|

   5:         Mspec.run({

   6:             :tool => mspec + '/tools/mspec-clr4.exe',

   7:             :reportdirectory => 'Reports',

   8:             :assembly => f

   9:         })

  10:     end

  11: end

 

Hier das von Alex bereitgestellte Ruby File, welches immer das Verzeichnis der aktuellsten Package Version zurückliefert. Anpassungen sind keine nötig.

   1: class NuGetLatest

   2:   attr_reader :path

   3:  

   4:   def initialize(package_name, packages_dir = Dir.pwd)

   5:     @package_name = package_name

   6:  

   7:     raise "#{packages_dir} is not a directory" unless File.directory? packages_dir

   8:  

   9:     Dir.chdir packages_dir do

  10:       candidates = Dir["#{package_name}*"]

  11:       raise "No package '#{package_name}' was found in #{packages_dir}" unless candidates.any?

  12:  

  13:       latest = find_latest_version candidates

  14:       @path = File.join packages_dir, latest

  15:     end

  16:   end

  17:  

  18:   def to_s

  19:     @path

  20:   end

  21:  

  22:   def +(other)

  23:     to_s + other

  24:   end

  25:  

  26:   private

  27:   def find_latest_version(candidates)

  28:     return candidates.first if candidates.length == 1

  29:  

  30:     latest = candidates.map { |path|

  31:       version = path.sub /^#{@package_name}./, ''

  32:       version = parse_version version

  33:  

  34:       {

  35:         :version => version,

  36:         :path => path

  37:       }

  38:     }.max { |left, right| left[:version] <=> right[:version] }

  39:  

  40:     latest[:path]

  41:   end

  42:  

  43:   def parse_version(version_string)

  44:     major, minor, patch, revision, special = version_string.match(/^(\d+)\.(\d+)\.(\d+)\.?(\d+)?-?(.+)?$/).captures

  45:     SemVer.new(major.to_i, minor.to_i, patch.to_i, revision.to_i, special)

  46:   end

  47:  

  48:   # Borrowed from the SemVer gem and enhanced to support .NET's 4-digit versioning system.

  49:   class SemVer

  50:     attr_accessor :major, :minor, :patch, :revision, :special

  51:  

  52:     def initialize(major = 0, minor = 0, patch = 0, revision = 0, special = '')

  53:       major.kind_of? Integer or raise "invalid major: #{major}"

  54:       minor.kind_of? Integer or raise "invalid minor: #{minor}"

  55:       patch.kind_of? Integer or raise "invalid patch: #{patch}"

  56:       revision.kind_of? Integer or raise "invalid revision: #{revision}"

  57:  

  58:       unless special.nil? or special.empty?

  59:         special =~ /[A-Za-z][0-9A-Za-z-]+/ or raise "invalid special: #{special}"

  60:       end

  61:  

  62:       @major, @minor, @patch, @revision, @special = major, minor, patch, revision, special

  63:     end

  64:  

  65:     def <=>(other)

  66:       maj = @major.to_i <=> other.major.to_i

  67:       return maj unless maj == 0

  68:  

  69:       min = @minor.to_i <=> other.minor.to_i

  70:       return min unless min == 0

  71:  

  72:       pat = @patch.to_i <=> other.patch.to_i

  73:       return pat unless pat == 0

  74:  

  75:       rev = @revision.to_i <=> other.revision.to_i

  76:       return rev unless rev == 0

  77:  

  78:       spe = @special <=> other.special

  79:       return spe unless spe == 0

  80:  

  81:       0

  82:     end

  83:  

  84:     include Comparable

  85:   end

  86: end

  87:  

  88: module Conversions

  89:   def NuGetLatest(package_name, packages_dir)

  90:     NuGetLatest.new(package_name, packages_dir)

  91:   end

  92: end

  93:  

  94: include Conversions

 

Hier die MSpec Klasse zum Ausführen der Unit Tests. Vorsicht: Das Skript prüft eine Umgebungsvariable und ist dediziert auf uns zugeschnitten (Einsatz von Team City). Hier müsstet ihr also Anpassungen vornehmen.

//Update: Wie ich inzwischen erfahren habe, macht MSpec die Prüfung auf die von Team City gesetzte Umgebungsvariable inzwischen implizit!

   1: class Mspec

   2:   def self.run(attributes)

   3:     tool = attributes.fetch(:tool)

   4:     reportDirectory = attributes.fetch(:reportdirectory, '.').to_absolute

   5:     assembly = attributes.fetch(:assembly).to_absolute

   6:     

   7:     reportFile = assembly.name.ext('html').in(reportDirectory).to_absolute

   8:     FileUtils.mkdir_p reportFile.dirname

   9:     

  10:     mspec = tool.to_absolute

  11:     

  12:     Dir.chdir(assembly.dirname) do

  13:       sh "#{mspec.escape} #{'--teamcity ' if ENV['TEAMCITY_PROJECT_NAME']}--timeinfo --html #{reportFile.escape} #{assembly.escape}"

  14:     end

  15:   end

  16: end

Mit Tag(s) versehen: , ,

3 thoughts on “Aktuelle NuGet Packages in Build Skripten verwenden

  1. Daniel Marbach (@danielmarbach) 7. März 2013 um 14:18 Reply

    Wow krass wieviel code. Ich dachte immer ruby sei so super sexy und simple. Hier die Poweshellvariante:

    Function GetNewest($path){
    $firstPart = $path.split(„*“)[0]
    $secondPart = $path.split(„*“)[1]

    $highestVersion = [version] „0.0“
    Get-Item $path | Foreach-Object {
    $currentVersion = [version] $_.fullname.Replace($firstPart, „“).Replace($secondPart, „“)
    if($currentVersion.CompareTo($highestVersion) -gt 0){
    $highestVersion = $currentVersion
    $newest = $_.fullname
    }
    }

    return $newest
    }

    Gefällt mir

    • Uli Armbruster 7. März 2013 um 15:55 Reply

      Bin persönlich auch kein großer Ruby Fan, aber da Alex uns bei unserem Build Server unterstützt hat, haben wir diese Variante gewählt, weil er sich hier gut auskennt. Mit PowerShell sieht das ziemlich geschmeidig aus.

      Alex hat das Ganze übrigens mit Tests unterlegt, wollte das dann aber nicht auch noch posten. Kann ich aber Interessierten gerne noch zukommen lassen.

      Gefällt mir

  2. Uli Armbruster 7. März 2013 um 16:37 Reply

    Den aktuellsten Code samt Specs gibt es bei Alex im Repo: https://github.com/agross/NuGetLatest

    Gefällt mir

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: