5

I have for the first time made something I want to share on NuGet, but I am not sure how this "release preparation" is done.

At the moment my following steps are:

  1. Manually change the assembly version of my main assembly (MyAssembly) in AssemblyInfo.cs from 1.0.0.0 to 1.1.0.0
  2. Build the main assembly release version in Visual Studio
  3. Pack the release for my main assembly (nuget pack -prop configuration=release == MyAssembly.1.1.0.0.nupkg)
  4. Publish this package to NuGet (nuget push MyAssembly.1.1.0.0.nupkg)
  5. Update the NuGet packages for assemblies referring MyAssembly (e.g. MyAssembly.Web.Mvc gets its MyAssembly updated from v1.0.0.0 -> v1.1.0.0 using the NuGet Package Manager) defined in packages.config
  6. Change the version AssemblyInfo.cs of MyAssembly.Web.Mvc to 1.1.0.0, build release, NuGet pack and publish
  7. Repeat step 6 for all my references assemblies

This is some steps that are a PITA, and I will some day make an error and break the release.

This got me think, am I missing a crucial point, am I doing all this wrong?

UPDATE

Based on the knowledge @ialekseev provided, I have now created this:

Build.ps1

Param(
    [Parameter(Mandatory=$true)]
    [string]$version,
    [string]$configuration = "Release",
    [boolean]$tests = $false,
    [boolean]$publish = $false,
    [boolean]$pack = $false,
    [string]$outputFolder = "build\packages"
)

# Include build functions
. "./BuildFunctions.ps1"

# The solution we are building
$solution = "NerveFramework.sln"
$assemblies = "NerveFramework", "NerveFramework.Web", "NerveFramework.Web.Mvc", "NerveFramework.Web.WebApi"

# Start by changing the assembly version
Write-Host "Changing the assembly versions to '$version'..."
$assemblyInfos = Get-ChildItem $assemblies -Filter "AssemblyInfo.cs" -Recurse | Resolve-Path -Relative
foreach ($assemblyInfo in $assemblyInfos) {
    ChangeAssemblyVersion $assemblyInfo $version
}

# Build the entire solution
Write-Host "Cleaning and building $solution (Configuration: $configuration)"
BuildSolution $solution $configuration

# Change dependency version on all depending assemblies
Write-Host "Changing the NerveFramework(s) NuGet Spec version dependencies to '$version'..."
$nuspecs = Get-ChildItem $assemblies -Filter "NerveFramework*.nuspec" -Recurse | Resolve-Path -Relative
foreach ($nuspec in $nuspecs) {
    ChangeNugetSpecDependencyVersion $nuspec "NerveFramework" $version
} 

# Pack the assemblies and move to output folder
if ($pack) {
    Write-Host "Packaging projects..."
    $projects = Get-ChildItem $assemblies -Filter "NerveFramework*.csproj" -Recurse | Resolve-Path -Relative
    foreach ($project in $projects) {
        PackProject $project $configuration $outputFolder
    } 
}

# Publish the assemblies
if ($publish) {
    Write-Host "Publishing packages..."
    $packages = Get-ChildItem $outputFolder -Filter "*$version.nupkg" -Recurse | Resolve-Path -Relative
    foreach ($package in $packages) {
        PublishPackage $package
    } 
}

BuildFunctions.ps1

Function BuildSolution() {

    Param(
        [Parameter(Mandatory=$true)]
        [string]$solution,
        [Parameter(Mandatory=$true)]
        [string]$configuration
    )

    # Set the path to the .NET folder in order to use "msbuild.exe"
    $env:PATH = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319"

    Invoke-Expression "msbuild.exe $solution /nologo /v:m /p:Configuration=$configuration /t:Clean"
    Invoke-Expression "msbuild.exe $solution /nologo /v:m /p:Configuration=$configuration /clp:ErrorsOnly"
}

Function ChangeAssemblyVersion() {

    Param(
        [Parameter(Mandatory=$true)]
        [string]$filePath,
        [Parameter(Mandatory=$true)]
        [string]$publishVersion
    )

    Write-Host "-- Updating '$filePath' to version '$publishVersion'"

    $assemblyVersionPattern = 'AssemblyVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)'
    $assemblyVersion = 'AssemblyVersion("' + $publishVersion + '")';
    $assemblyFileVersionPattern = 'AssemblyFileVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)'
    $assemblyFileVersion = 'AssemblyFileVersion("' + $publishVersion + '")';

    (Get-Content $filePath -Encoding utf8) | ForEach-Object { 
            % { $_ -Replace $assemblyVersionPattern, $assemblyVersion } |
            % { $_ -Replace $assemblyFileVersionPattern, $assemblyFileVersion } 
    } | Set-Content $filePath
}

Function ChangeNugetSpecDependencyVersion() {

    Param(
        [Parameter(Mandatory=$true)]
        [string]$filePath,
        [Parameter(Mandatory=$true)]
        [string]$packageId,
        [Parameter(Mandatory=$true)]
        [string]$publishVersion
    )

    [xml] $toFile = (Get-Content $filePath)
    $nodes = $toFile.SelectNodes("//package/metadata/dependencies/dependency[starts-with(@id, $packageId)]")
    if ($nodes) {
        foreach ($node in $nodes) {
            $nodeId = $node.id
            Write-Host "-- Updating '$nodeId' in '$filePath' to version '$publishVersion'"
            $node.version = "[" + $publishVersion +"]"
            $toFile.Save($filePath)
        }
   }
}

Function PackProject() {

    Param(
        [Parameter(Mandatory=$true)]
        [string]$project,
        [Parameter(Mandatory=$true)]
        [string]$configuration,
        [Parameter(Mandatory=$true)]
        [string]$outputFolder
    )

    if (!(Test-Path -Path $outputFolder)) {
        New-Item $outputFolder -Type Directory
    }

    Write-Host "-- Packaging '$project'"
    Invoke-Expression ".nuget\NuGet.exe pack $project -OutputDirectory '$outputFolder' -Prop Configuration=$configuration"
}

Function PublishPackage() {

    Param(
        [Parameter(Mandatory=$true)]
        [string]$package
    )

    Write-Host "-- Publishing '$package'"
    Invoke-Expression ".nuget\NuGet.exe push $package"

}

Run like .\Build -version 1.2.0.0 -pack $true

janhartmann
  • 14,713
  • 15
  • 82
  • 138

1 Answers1

2

You need to automate you procedure, because, yes, doing it manually is very error-prone process.

I usually write powershell/cmd scripts that could perform the following steps:

  1. Build solution
  2. Run tests over it
  3. Update assemblies versions
  4. Create NuGet packages
  5. Publish NuGet packages

Then you could just run it locally to go through all these steps automatically. Also you could delegate some tasks to your Build-server (if you have one). E.g. Teamcity could take responsibility for building, running tests, and even publishing packages to NuGet. Anyway, I like to have all these scripts in my source control to be able to run them locally whenever I want to. If you are interested, you could review scripts I wrote for one of my projects on GitHub. Scripts are in build folder and publish_local.cmd is an entry point. Project has multiple NuGet dependencies, so, I believe it's similar to what you are trying to accomplish.

I know it's better to provide examples directly in the answer, but there are too many scripts to include here. If you have any questions, please be my guest.

alekseevi15
  • 1,732
  • 2
  • 16
  • 20
  • Interesting, I see if I can figure this out. But at a eye glance I seems to do what I need exactly. I let you know if I run into problems, but it seems good! :-) – janhartmann Jan 28 '15 at 08:44
  • Hi again, I've updated my question with a powershell script, but somehow the build process on the assemblies that has a dependency on my other assembly can not build - says missing assembly or reference. Any suggestions? If you have time you can see my project here: https://github.com/janhartmann/nerve-framework – janhartmann Jan 28 '15 at 20:02
  • @janhartmann, I've just forked your project and I can't build it in VS, because NerveFramework.Web.WebApi does not reference NerveFramework.Web. In your solution you _should have_ references between projects to build them properly. But when creating NuGet packages, you could just create package ...WebApi and specify ...Web package as a NuGet dependency in nuspec file(given that ...Web is presented in NuGet as well). – alekseevi15 Jan 29 '15 at 07:35
  • Damnit. My bad. Stay tuned, i need to get of work first in order to fix that issue. – janhartmann Jan 29 '15 at 07:44
  • First time I'm releasing something, should I just add it as reference with "Add reference..." dialog? Sorry for getting out of context. – janhartmann Jan 29 '15 at 07:52
  • @janhartmann, yes, "Add Reference -> Projects" and choose your project. Basically, your projects within your solution should reference each other in a normal way (not from NuGet). Forget about NuGet for a moment - you just have solution with projects that reference each other. There is no sense to load your _own_ projects that you have _at hand_ from NuGet. You only need NuGet to _load_ external dependencies(like EF), and when you _create_ NuGet packages. May be it would be easier for you to clone my project to see how dependencies between adapters and Core library are organized. – alekseevi15 Jan 29 '15 at 08:16
  • Will do, thank you. Off from work in six hours. Then ill look into it. Thanks for your help for now! ;-) – janhartmann Jan 29 '15 at 08:17
  • Ok, seems like solving those dependency issues fixed the build. Also removed the dependencies in packages.config and moved them to the *.nuspec. Now the only issue remain is the packaging, it tells me there is no replacement variables. Pushed to Git. Thanks for your time. – janhartmann Jan 29 '15 at 15:46
  • @janhartmann, I've just made few slight changes to get rid of the "replacement variables" problem. Check it out in pull-request. The point here is that if you have some variable(ex, $id) in nuspec file, you need to provide it when performing packaging. Now you have another problem(yellow warning) that your packages are not in _lib_ folder. But this problem is a clear one. – alekseevi15 Jan 30 '15 at 09:18
  • Great thanks, lets continue there (if there is more) Thanks for your help! – janhartmann Jan 30 '15 at 09:19