dotnet / Nerdbank.GitVersioning

Stamp your assemblies, packages and more with a unique version generated from a single, simple version.json file and include git commit IDs for non-official builds.
https://www.nuget.org/packages/Nerdbank.GitVersioning
MIT License
1.38k stars 167 forks source link

When multiple csproj files are in the same folder, binaries are sometimes not versioned during their build #1101

Closed mslukebo closed 1 month ago

mslukebo commented 1 month ago

I have a solution where two csproj files share the same directory. I have discovered that sometimes executables created during a dotnet build are sometimes not correctly versioned.

I narrowed this down to the below minimal reproduction:

  1. Create a new solution
  2. Create two projects (I used .NET 8 console apps) within the same directory with the following names
    • nbgv.test.a
    • nbgv.test.b
  3. Update the csproj for the two created projects with the following:

    
    <Project Sdk="Microsoft.NET.Sdk">
    
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <OutputType>Exe</OutputType>
        <StartupObject />
        <RunPostBuildEvent>Always</RunPostBuildEvent>
        <LangVersion>latest</LangVersion>
    </PropertyGroup>
    
    <ItemGroup>
        <PackageReference Include="Nerdbank.GitVersioning"
                          Version="3.6.143"
                          PrivateAssets="all"
                          Condition="!Exists('packages.config')" />
    </ItemGroup>

4. Add a `version.json` with some expected version number. Here is mine:
```json
{
    "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
    "version": "11.8"
  }

Now, repeatedly build these projects. Sometimes the version is set correctly, but randomly it'll fail to have the correct version. The file with the incorrect version is not deterministic.

Here is a small powershell script I wrote to test this:

$src = "F:\repos\nbgv-test"
$exePrefix = "nbgv.test"
$expectedMajorVersion = "18"

function Run-Test() {

    # Run git clean on directory $src
    Write-Host "Running git clean on directory $src`n"
    Set-Location -Path $src
    git clean -fdx | Out-Null 

    # Run dotnet build on directory $src
    Write-Host "Running dotnet restore on directory $src`n"
    dotnet restore $src | Out-Null 

    Write-Host "Running dotnet build on directory $src`n"
    dotnet build $src -c Release --no-restore | Out-Null;

    # Find all output EXEs
    Write-Host "Finding all files like '$($exePrefix)*.exe' in directory $src"
    $files = Get-ChildItem -Path $src -Filter "$($exePrefix)*.exe" -Recurse

    $hasError = $false

    foreach ($file in $files) {
        $versionInfo = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($file.FullName)
        Write-Host "File $($file.FullName) has version $($versionInfo.FileVersion)"

        if ($versionInfo.FileVersion -notlike "$($expectedMajorVersion)*") {
            $hasError = $true
        }
    }

    if ($hasError)
    {
        return 1
    }

    return 0
}

Clear-Host

Write-Host "`n================================="
Write-Host "STARTING TEST`n"

$i = 0

while ($true)
{
    $i += 1
    Write-Host "STARTING ITERATION $i`n"

    $result = Run-Test

    if ($result -ne 0)
    {
        break;
    }
}

Write-Host "FAILED AFTER $i ITERATIONS"
Write-Host "`n================================="

Note that this script is building the entire solution, but the problem repros when building individual csproj files as well.

Is there a workaround to this problem?

AArnott commented 1 month ago

I have a solution where two csproj files share the same directory.

I'm gonna stop you right there (literally... I haven't read any more than that in your description) because that's a terrible idea. By default MSBuild writes to bin and obj directories under the project directory, assuming that they have an isolated location for outputs. For example package restore writes to obj\project.assets.json, and if you have two projects in the same directory, they'll stomp on each other's file, wreaking havoc on your build.

So I'm not really interested in improving how NB.GV works in an environment that is so fundamentally broken that many other problems are likely to happen as well.