Come posso incrementare automaticamente la versione dell'assembly C # tramite la nostra piattaforma CI (Hudson)?


112

Io e il mio gruppo siamo orrendi nell'incrementare i numeri di versione degli assembly e spesso spediamo gli assembly con le versioni 1.0.0.0. Ovviamente, questo provoca molti mal di testa.

Stiamo migliorando molto con le nostre pratiche tramite la nostra piattaforma CI e mi piacerebbe davvero impostarlo per aumentare automaticamente i valori all'interno del assemblyinfo.csfile in modo che le versioni dei nostri assembly vengano aggiornate automaticamente con le modifiche al codice in quell'assembly.

In precedenza avevo impostato (prima di trovare Hudson ) un modo per aumentare il valore tramite una msbuildo la riga di comando (non ricordo), ma con Hudson, questo aggiornerà il repository SVN e attiverà UN ALTRO build. Ciò si tradurrebbe in un ciclo infinito lento poiché Hudson interroga SVN ogni ora.

Avere Hudson che incrementa il numero di versione è una cattiva idea? Quale sarebbe un modo alternativo per farlo?

Idealmente, i miei criteri per una soluzione sarebbero quelli che:

  • Incrementa il numero di build assemblyinfo.csprima di una build
  • Aumenta solo il numero di build negli assembly che sono stati modificati. Ciò potrebbe non essere possibile poiché Hudson cancella la cartella del progetto ogni volta che esegue una build
  • Salva il file assemblyinfo.cs modificato nel repository di codice (attualmente VisualSVN )
  • Non fa sì che Hudson attivi una nuova build la prossima volta che esegue la scansione per le modifiche

Elaborando questo nella mia testa, potrei facilmente trovare una soluzione alla maggior parte di questo tramite file / comandi batch, ma tutte le mie idee farebbero sì che Hudson avvii una nuova build la prossima volta che esegue la scansione. Non sto cercando qualcuno che faccia tutto per me, mi metta solo nella giusta direzione, forse una tecnica per convincere Hudson a ignorare determinati impegni SVN, ecc.

Tutto quello che ho trovato finora è solo un articolo che spiega come ottenere il numero di versione incrementato automaticamente, nulla tiene conto di una piattaforma CI che potrebbe essere trasformata in un ciclo infinito.

Risposte:


63

Una semplice alternativa consiste nel consentire all'ambiente C # di incrementare la versione dell'assembly impostando l'attributo version su major.minor.*(come descritto nel modello di file AssemblyInfo).

Tuttavia, potresti cercare una soluzione più completa.

EDIT (Risposta alla domanda in un commento):

Da AssemblyInfo.cs:

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]

Non l'ho mai visto prima, potresti entrare un po 'più in dettaglio su cosa fa. Funziona solo all'interno di un IDE o funziona con un intero team di sviluppatori con una piattaforma CI?
Allen Rice

ahhh l'ho già visto, potrebbe essere una soluzione accettabile, ma # built non è memorizzato in subversion ecc. Ho configurato Hudson per archiviare i file e in questo modo è memorizzato in modo che potrebbe essere accettabile. Dovrò fare qualche ricerca in più su come funziona quel meccanismo, grazie! Non sapresti come determina cosa inserire come valori, vero?
Allen Rice

1
Vedi la mia risposta qui sotto per la risposta alla tua domanda. I valori sono determinati in base al tempo di compilazione.
Kyle Trauberman

Wow, penso che funzionerà. Non sono sicuro di come abbiamo trascurato una soluzione così semplice
Allen Rice

Spero che lo faccia, felice di poterti aiutare. Perché fare qualcosa nel modo più difficile quando il modo facile e veloce è anche il modo giusto? :)
Greg D

65

Ecco cosa ho fatto, per timbrare l'attributo AssemblyFileVersion.

Rimosso AssemblyFileVersion da AssemblyInfo.cs

Aggiungere un nuovo file vuoto denominato AssemblyFileInfo.cs al progetto.

Installa il set di strumenti delle attività della community di MSBuild nel computer di compilazione Hudson o come dipendenza NuGet nel tuo progetto.

Modifica il file del progetto (csproj), è solo un file msbuild e aggiungi quanto segue.

Da qualche parte ci sarà una <PropertyGroup>dichiarazione della versione. Cambia quello in modo che legga ad es

 <Major>1</Major>
 <Minor>0</Minor>
 <!--Hudson sets BUILD_NUMBER and SVN_REVISION -->
 <Build>$(BUILD_NUMBER)</Build>
 <Revision>$(SVN_REVISION)</Revision>

Hudson fornisce quelle variabili env che vedi lì quando il progetto è costruito su hudson (supponendo che sia stato recuperato da subversion).

In fondo al file di progetto, aggiungi

 <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')" />
  <Target Name="BeforeBuild" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')">
    <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
    <AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyFileInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyConfiguration="$(Configuration)" Condition="$(Revision) != '' " />
  </Target>

Questo utilizza MSBuildCommunityTasks per generare AssemblyFileVersion.cs per includere un attributo AssemblyFileVersion prima della compilazione del progetto. Puoi farlo per qualsiasi / tutti gli attributi della versione, se vuoi.

Il risultato è che, ogni volta che si emette una build hudson, l'assembly risultante ottiene un AssemblyFileVersion di 1.0.HUDSON_BUILD_NR.SVN_REVISION ad esempio 1.0.6.2632, che significa la sesta build # in hudson, ricavato dalla revisione 2632 di subversion.


1
Quindi, solo per aggiornare questo: il metodo funziona per C #. Lo uso da tempo. Ma gli assembly C ++ (cioè C ++ / CLI) sono ancora un problema. Per quanto ne so, l'attività AssemblyInfo non produce C ++ valido. Inoltre, penso che questo metodo abbia un leggero svantaggio in quanto è un po 'opaco per gli altri sviluppatori capire cosa sta succedendo. Peccato che non sia possibile inserire i numeri di versione direttamente in MSBuild come proprietà ...
CJBrew

@CJBrew Potresti semplicemente creare un piccolo file .bat che produce il codice C ++ per AssemblyInfo e fare in modo che msbuild dia il via a quello scipt. Non sono sicuro di cosa intendi spingerlo come proprietà, potresti certamente inserire le stringhe di versione in qualsiasi proprietà che ti piace - non è necessario usare la major / minor / build / revision che ho usato qui.
n.

È stato ottenuto qualcosa utilizzando questo percorso rispetto al semplice commento di AssemblyFileVersion e lasciare che venga impostato per corrispondere automaticamente a [assembly: AssemblyVersion ("1.0. *")]?
cchamberlain

@ColeChamberlain Questo aumenterà automaticamente se lo costruisci da Visual Studio sul tuo PC, non da Hudson - e non c'è relazione con il numero di versione e una particolare build e revisione del codice sorgente.
nn

42

Ecco una soluzione elegante che richiede un po 'di lavoro in anticipo quando si aggiunge un nuovo progetto ma gestisce il processo molto facilmente.

L'idea è che ogni progetto si colleghi a un file Solution che contiene solo le informazioni sulla versione dell'assembly. Quindi il tuo processo di compilazione deve solo aggiornare un singolo file e tutte le versioni dell'assembly estraggono da un file al momento della compilazione.

passi:

  1. Aggiungi una classe al file della soluzione * .cs, ho chiamato min SharedAssemblyProperties.cs
  2. Rimuovi tutte le informazioni cs da quel nuovo file
  3. Taglia le informazioni sull'assembly da un file AssemblyInfo: [assembly: AssemblyVersion ("1.0.0.0")] [assembly: AssemblyFileVersion ("1.0.0.0")]
  4. Aggiungere l'istruzione "using System.Reflection;" al file e quindi incolla i dati nel tuo nuovo file cs (ex SharedAssemblyProperties.cs)
  5. Aggiungi un elemento esistente al tuo progetto (aspetta ... continua a leggere prima di aggiungere il file)
  6. Seleziona il file e prima di fare clic su Aggiungi, fai clic sul menu a discesa accanto al pulsante Aggiungi e seleziona "Aggiungi come collegamento".
  7. Ripetere i passaggi 5 e 6 per tutti i progetti esistenti e nuovi nella soluzione

Quando aggiungi il file come collegamento, memorizza i dati nel file di progetto e al momento della compilazione estrae le informazioni sulla versione dell'assembly da questo file.

Nel controllo del codice sorgente, aggiungi un file bat o un file script che incrementa semplicemente il file SharedAssemblyProperties.cs e tutti i tuoi progetti aggiorneranno le informazioni sull'assembly da quel file.


grazie, Mark. Ci scusiamo per il collegamento morto, si scopre che il server della comunità non è così facile da spostare. Dovrei cercare aiuto su questo argomento ...
sondlerd

11

Hudson può essere configurato per ignorare le modifiche a determinati percorsi e file in modo che non richieda una nuova build.

Nella pagina di configurazione del lavoro, in Gestione codice sorgente , fare clic sul pulsante Avanzate . Nella casella Regioni escluse immettere una o più espressioni regolari per trovare la corrispondenza con le esclusioni.

Ad esempio, per ignorare le modifiche al file version.properties è possibile utilizzare:

/MyProject/trunk/version.properties

Funzionerà per linguaggi diversi da C # e ti consente di archiviare le informazioni sulla versione all'interno di subversion.


1
Hudson può anche ignorare i commit di determinati utenti o non attivare una build a seconda del messaggio di commit. In questo modo puoi ignorare tutti i commit di Hudson.
Peter Schuetze

9

.NET lo fa per te. Nel file AssemblyInfo.cs, impostare la versione dell'assembly su major.minor. * (Ad esempio: 1.0. *).

Quando crei il tuo progetto, la versione viene generata automaticamente.

I numeri di build e revisione sono generati in base alla data, usando l'epoca unix, credo. La build si basa sul giorno corrente e la revisione si basa sul numero di secondi trascorsi dalla mezzanotte.


21
<ring, ring> "ciao, supporto del prodotto come posso aiutarti?" <cliente> "ho un errore" <supporto> "ok, quale versione stai utilizzando?" <cliente> "versione uno punto due revisione otto cinque due cinque tre sette quattro build sette quattro sei tre cinque due nove ..." <supporto> "aspetta, digita solo che ... hmmm ... ripeti la versione numeri, sembra che non abbiamo quella build e quella revisione elencate ... "- GRRR!
Jimbo

haha bel commento. Nemmeno io sono un fan di quel sistema di incremento: p
Joshua Hayes

3
L'autoincremento nello studio visivo fa schifo sul serio.
Kugel

8
@Jimbo: anche se siamo tutti d'accordo che il tuo commento è stato divertente, in pratica non ha importanza. Quando parli della tua installazione VS, hai Visual Studio 2008 SP1 o VS2008 9.0.30729.1 SP? L'utilizzo dei numeri di build con incremento automatico è uno schema molto comune e può essere facilmente "risolto" incrementando i numeri di versione maggiore / minore quando esce una build di rilascio.
Marek

il più alto che abbiamo ottenuto con un numero di build è 678 prima di resettarlo a 0 per un aumento della revisione minore (ovviamente cruisecontrol, sembrava più facile da ripristinare rispetto a hudson come in cruisecontrol, sei appena entrato e lo hai salvato di nuovo a 0 nel progetto , ma tutto il resto in Hudson è migliore)
Dean Hiller

8

Non ho mai visto che la funzionalità 1.0. * Funzioni in VS2005 o VS2008. C'è qualcosa che deve essere fatto per impostare VS per aumentare i valori?

Se AssemblyInfo.cs è hardcoded con 1.0. *, Dove sono archiviate la build / revisione reale?

Dopo aver inserito 1.0. * In AssemblyInfo, non possiamo utilizzare la seguente istruzione perché ProductVersion ora ha un valore non valido: utilizza 1.0. * E non il valore assegnato da VS:

Version version = new Version(Application.ProductVersion);

Sigh - questa sembra essere una di quelle cose che tutti chiedono ma in qualche modo non c'è mai una risposta solida. Anni fa ho visto soluzioni per generare un numero di revisione e salvarlo in AssemblyInfo come parte di un processo di post-compilazione. Speravo che quel tipo di danza non sarebbe stato richiesto per VS2008. Forse VS2010?


10
È necessario rimuovere AssemblyFileVersion. A parte questo, è fantastico per noi, la risposta accettata è.
Allen Rice

1
Sì, la rimozione di AssemblyFileVersion consente l'aggiornamento della versione e non più errori con Version. Bello. Nota: due operazioni di compilazione incrementano la revisione solo una volta, ma se ricostruisci la revisione viene aggiornata. Come ha detto ktrauberman, sembra build.revision = date.time, il che spiega perché i dati non sono memorizzati da nessuna parte tranne che nell'assembly. Ora ho bisogno di ottenere un'installazione MSI standard per generare un nuovo ProductCode quando il progetto di output principale viene aggiornato. Le impostazioni non consentono la revisione, ma solo la compilazione. Voglio eseguire l'installazione su un'installazione esistente per eseguire un aggiornamento. Necessità di ricerca.
TonyG

5

Presumo che si possa farlo anche con un modello di testo in cui si creano al volo gli attributi di assembly in questione dall'ambiente come AssemblyVersion.tt fa di seguito.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("SVN_REVISION");
revision = revision == null ? "0" : int.Parse(revision).ToString();    
#>
using System.Reflection;
[assembly: AssemblyVersion("1.0.<#=build#>.<#=revision#>")]
[assembly: AssemblyFileVersion("1.0.<#=build#>.<#=revision#>")]

3

Come continuazione della risposta di MikeS, volevo aggiungere che VS + Visual Studio Visualization and Modeling SDK deve essere installato affinché funzioni e che è necessario modificare anche il file di progetto. Va anche menzionato che uso Jenkins come server di build in esecuzione su un server Windows 2008 R2 con modulo versione, dove ottengo BUILD_NUMBER.

Il mio file di modello di testo version.tt ha questo aspetto

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("_BuildVersion");
revision = revision == null ? "5.0.0.0" : revision;    
#>
using System.Reflection;
[assembly: AssemblyVersion("<#=revision#>")]
[assembly: AssemblyFileVersion("<#=revision#>")]

Ho quanto segue nei gruppi di proprietà

<PropertyGroup>
    <TransformOnBuild>true</TransformOnBuild>
    <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

dopo l'importazione di Microsoft.CSharp.targets, ho questo (a seconda di dove installi VS

<Import Project="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />

Sul mio server di build ho quindi il seguente script per eseguire la trasformazione del testo prima della build effettiva, per ottenere l'ultimo numero di changeset su TFS

set _Path="C:\Build_Source\foo"

pushd %_Path% 
"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" history . /r /noprompt /stopafter:1 /Version:W > bar
FOR /f "tokens=1" %%foo in ('findstr /R "^[0-9][0-9]*" bar') do set _BuildVersion=5.0.%BUILD_NUMBER%.%%foo
del bar
popd

echo %BUILD_NUMBER%
echo %_BuildVersion%
cd C:\Program Files (x86)\Jenkins\jobs\MyJob\workspace\MyProject
MSBuild MyProject.csproj /t:TransformAll 
...
<rest of bld script>

In questo modo posso tenere traccia delle build E dei changeset, quindi se non ho controllato nulla dall'ultima build, l'ultima cifra non dovrebbe cambiare, tuttavia potrei aver apportato modifiche al processo di build, da qui la necessità del penultimo numero . Ovviamente se effettui più check-in prima di una build, ottieni solo l'ultima modifica riflessa nella versione. Immagino che potresti concatenare ciò che è richiesto.

Sono sicuro che puoi fare qualcosa di più elaborato e chiamare TFS direttamente dall'interno del modello tt, tuttavia questo funziona per me.

Posso quindi ottenere la mia versione in fase di esecuzione in questo modo

Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;

1

La mia soluzione non richiede l'aggiunta di strumenti esterni o linguaggi di scripting: è praticamente garantito che funzioni sulla tua macchina di compilazione. Risolvo questo problema in più parti. Innanzitutto, ho creato un file BUILD.BAT che converte il parametro BUILD_NUMBER di Jenkins in una variabile di ambiente. Uso la funzione "Esegui comando batch di Windows" di Jenkins per eseguire il file batch di build inserendo le seguenti informazioni per la build di Jenkins:

     ./build.bat --build_id %BUILD_ID% -build_number %BUILD_NUMBER%

Nell'ambiente di compilazione, ho un file build.bat che inizia come segue:

     rem build.bat
     set BUILD_ID=Unknown
     set BUILD_NUMBER=0
     :parse_command_line
     IF NOT "%1"=="" (
         IF "%1"=="-build_id" (
             SET BUILD_ID=%2
             SHIFT
             )
         IF "%1"=="-build_number" (
             SET BUILD_NUMBER=%2
             SHIFT
         )
         SHIFT
         GOTO :parse_command_line
     )
     REM your build continues with the environmental variables set
     MSBUILD.EXE YourProject.sln

Dopo averlo fatto, ho fatto clic con il pulsante destro del mouse sul progetto da creare nel riquadro Esplora soluzioni di Visual Studio e ho selezionato Proprietà, selezionare Eventi di compilazione e ho inserito le seguenti informazioni come Riga di comando dell'evento di pre-compilazione, che crea automaticamente un file .cs contenente informazioni sul numero di build in base alle impostazioni delle variabili di ambiente correnti:

     set VERSION_FILE=$(ProjectDir)\Properties\VersionInfo.cs
     if !%BUILD_NUMBER%==! goto no_buildnumber_set
     goto buildnumber_set
     :no_buildnumber_set
     set BUILD_NUMBER=0
     :buildnumber_set
     if not exist %VERSION_FILE% goto no_version_file
     del /q %VERSION_FILE%
     :no_version_file
     echo using System.Reflection; >> %VERSION_FILE%
     echo using System.Runtime.CompilerServices; >> %VERSION_FILE%
     echo using System.Runtime.InteropServices; >> %VERSION_FILE%
     echo [assembly: AssemblyVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%
     echo [assembly: AssemblyFileVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%

Potrebbe essere necessario adattarti al tuo gusto di costruzione. Creo il progetto manualmente una volta per generare un file Version.cs iniziale nella directory Proprietà del progetto principale. Infine, includo manualmente il file Version.cs nella soluzione Visual Studio trascinandolo nel riquadro Esplora soluzioni, sotto la scheda Proprietà per quel progetto. Nelle build future, Visual Studio legge quindi il file .cs al momento della compilazione di Jenkins e ne ricava le informazioni corrette sul numero di build.


1

Quindi, abbiamo un progetto con una soluzione che contiene diversi progetti con assembly con numeri di versione diversi.

Dopo aver esaminato molti dei metodi precedenti, ho appena implementato un passaggio di compilazione per eseguire uno script Powershell che esegue una ricerca e sostituzione sul file AssemblyInfo.cs. Uso ancora il numero di versione 1.0. * Nel controllo del codice sorgente e Jenkins aggiorna manualmente il numero di versione prima che msbuild venga eseguito.

dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyFileVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyFileVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }

Ho aggiunto l'opzione -Encoding "UTF8" perché git ha iniziato a trattare il file .cs come file binari se non l'ho fatto. Certo, questo non aveva importanza, dal momento che non ho mai effettivamente eseguito il commit del risultato; è appena emerso mentre stavo testando.

Il nostro ambiente CI ha già una funzione per associare la build di Jenkins con il particolare commit git (grazie al plug-in Stash!), Quindi non mi preoccupo che non ci sia alcun commit git con il numero di versione allegato.


0

Questo è un meccanismo più semplice. Implica semplicemente l'aggiunta di un passaggio di compilazione dell'attività di comando batch di Windows prima del passaggio di MSBuild e l'uso di un semplice programma di ricerca e sostituzione (FART).

La fase batch

fart --svn -r AssemblyInfo.cs "[assembly: AssemblyVersion(\"1.0.0.0\")]" "[assembly: AssemblyVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
fart --svn -r AssemblyInfo.cs "[assembly: AssemblyFileVersion(\"1.0.0.0\")]" "[assembly: AssemblyFileVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
exit /b 0

Se stai usando un controllo del codice sorgente diverso da svn, modifica l'opzione --svn per quella appropriata per il tuo ambiente scm.

Scarica Fart


0

Ho deciso di utilizzare un paio di metodi utilizzando uno script Powershell pre-compilato ( https://gist.github.com/bradjolicoeur/e77c508089aea6614af3 ) per incrementare ogni build di successo, quindi in Global.asax ho fatto qualcosa del genere:

  // We are using debug configuration, so increment our builds.
  if (System.Diagnostics.Debugger.IsAttached)
  {
      string version = System.Reflection.Assembly.GetExecutingAssembly()
                                                       .GetName()
                                                       .Version
                                                       .ToString();

      var psi = new ProcessStartInfo(@"svn", "commit -m \"Version: " + version + "\n \"");
      psi.WorkingDirectory = @"C:\CI\Projects\myproject";
      Process.Start(psi); 
  }

Penso ancora che l'intero processo sia troppo complicato e cercherò un metodo più efficiente per ottenere lo stesso risultato. Lo volevo principalmente per passare la versione in SVN e poi in Jenkin senza troppi strumenti aggiuntivi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.