Alterando a versão do Assembly Info com TFS Build

Semana passada prestando consultoria para um cliente recebemos uma demanda para automatizar o Build de suas aplicações usando o TFS 2010 e um dos requisitos solicitados era que a cada build gerado fosse atualizado o arquivo Assembly Info

Analisando o arquivo Assembly Info temos as chaves AssemblyVersionAssemblyFileVersion que são responsáveis por referênciar a versão da DLL e a versão do arquivo respectivamente

Para atender esse requisito tivemos que alterar o workflow do Build Process Template criando uma nova atividade que deverá seguir o seguinte processo:

Criando o projeto

Agora vamos criar uma nova Class Library chamada CustomBuildDefinitions e outra chamada CustomBuildDefinitions.Template

Adicione ao projeto CustomBuildDefinitions as seguintes referências conforme abaixo

Adicione ao projeto CustomBuildDefinitions uma classe chamada UpdateAssemblyVersion.cs conforme abaixo

Agora vamos codificar a nossa classe, conforme abaixo:

[sourcecode language=”csharp”]
/// <summary>
/// Classe herdando de CodeActivity
/// </summary>
[BuildActivity(HostEnvironmentOption.Agent)]
public sealed class UpdateAssemblyVersion : CodeActivity
{
/// <summary>
/// Nome do arquivo
/// </summary>
[RequiredArgument]
public InArgument<string> AssemblyInfoFileMask { get; set; }

/// <summary>
/// Variável SourcesDirectory onde está or arquivos do build
/// </summary>
[RequiredArgument]
public InArgument<string> SourcesDirectory { get; set; }

/// <summary>
/// Workspace usado pelo build
/// </summary>
[RequiredArgument]
public InArgument<Workspace> Workspace { get; set; }

/// <summary>
/// Método que será executado quando a atividade for chamada
/// </summary>
/// <param name="context">Contexto do Build</param>
protected override void Execute(CodeActivityContext context)
{
this.CheckOut(context);
this.IncreaseAssemblyVersion(context);
this.CheckIn(context);
}

/// <summary>
/// Método responsável por realizar o Check-Out do arquivo
/// </summary>
/// <param name="context">Contexto do Build</param>
private void CheckOut(CodeActivityContext context)
{
// Recupera o argumento AssemblyInfoFileMask passado pelo build
string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
Workspace workspace = context.GetValue(this.Workspace);

// Para cada pasta que está no workspace
foreach (var folder in workspace.Folders)
{
// Procura no workspace os aquivos que contém a extensão da variável assemblyInfoFileMask
foreach (var file in Directory.GetFiles(folder.LocalItem, assemblyInfoFileMask, SearchOption.AllDirectories))
{
// Realiza o Check-Ou do arquivo
workspace.PendEdit(file);
}
}
}

/// <summary>
/// Método responsável por realizar o Check-In do arquivo
/// The ***NO_CI*** comment ensures that the CI build is not triggered (and that you end in an endless loop)
/// </summary>
/// <param name="context">Contexto do Build</param>
private void CheckIn(CodeActivityContext context)
{
// Recupera o argumento Workspace passado pelo build
Workspace workspace = context.GetValue(this.Workspace);

// Realiza o Check-In de todos os arquivos pendentes
workspace.CheckIn(workspace.GetPendingChanges(), "Build Agent", "***NO_CI***", null, null, new PolicyOverrideInfo("Auto checkin", null), CheckinOptions.SuppressEvent);
}

/// <summary>
/// Método responsável por atualizar a versão das chaves AssemblyVersion e AssemblyFileVersion
/// </summary>
/// <param name="context">Contexto do Build</param>
private void IncreaseAssemblyVersion(CodeActivityContext context)
{
// Recupera o argumento SourcesDirectory passado pelo build
string sourcesDirectory = context.GetValue(this.SourcesDirectory);
// Recupera o argumento AssemblyInfoFileMask passado pelo build
string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);

// Enumerador com todos os atributos que queremos alterar
foreach (string attribute in new string[] { "AssemblyVersion", "AssemblyFileVersion" })
{
// Expressão regular para localizar a versão
Regex regex = new Regex(attribute + @"\(""\d+\.\d+\.\d+\.\d+""\)");

// Recupera todos os arquivos AssemblyInfo
foreach (string file in Directory.EnumerateFiles(sourcesDirectory, assemblyInfoFileMask, SearchOption.AllDirectories))
{
// Le o arquivo AssemblyInfo
string text = File.ReadAllText(file);

// Localiza o atributo
Match match = regex.Match(text);

// Verifica se achou o atributo
if (match.Success)
{
// Recupe o número da versão
string versionNumber = match.Value.Substring(attribute.Length + 2, match.Value.Length – attribute.Length – 4);

// Incrementa a revisão do build
Version version = new Version(versionNumber);
Version newVersion = new Version(version.Major, version.Minor, version.Build, version.Revision + 1);

// Substitui a versão antiga para a nova
string newText = regex.Replace(text, attribute + "(\"" + newVersion.ToString() + "\")");

// Salva o novo valor no arquivo AssemblyInfo
File.WriteAllText(file, newText);
}
}
}
}
}
[/sourcecode]

O código está todo comentado facilitando o entendimento do mesmo

Depois de compilado o projeto, devemos copiar a DLL gerada para uma pasta separada do projeto, pois vamos utilizar depois na configuração do servidor de build

Alterando o Build Process Template

Depois de criado nossa atividade tempos que chamar essa nova atividade em nosso Build Process Template, para isso copie o arquivo DefaultTemplate.xaml que se encontra em Source Control -> BuildProcessTemplates para dentro do projeto CustomBuildDefinitions.Template e renomeie para CustomTemplate.xaml

Agora abra o arquivo CustomTemplate.xaml e vamos criar os seguintes argumentos para passar como parâmetro para nossa atividade

Agora localize a atividade GetWorkSpace e logo após adicione a atividade customizada no workflow

Agora nas propriedades da atividade configure conforme abaixo:

Configurando o TFS Build

Para que o TFS encontre essa nossa atividade customizada temos que configurar o controller no servidor de Build, para isso abra o Team Foundation Server Administration Console e selecione a opção Build Configuration e clique nas propriedades do Default Controller

Na opção Version control path to custom assemblies selecione a a pastaonde copiamos a DLL anteriormente, conforme abaixo

Criando um novo Build

Agora vamos criar um novo Build Definition onde vamos selecionar o nosso novo Build Template que customizamos, na guia Process clique em New

Agora selecione a opção Select an existing XAML file e clique em Browse

Selcione o nosso template que customizamos

Perceba que a nossa variável que criamos está aparecendo nas opções

 

Depois de configurado as outras opções do build vamos executa-lo e veja no log o resultado

Agora vamos ver o resultado do build

Referências

Ewald Hofman – Customize Team Build 2010 – Part 5- Increase Assembly Version

Deixe seu comentário, opinião, crítica

Aquele abraço!

Sobre Leandro Prado

Leandro Silveira Prado é Premier Field Engineer na Microsoft especializado em Application Lifecycle Management.