Wasn't sure if this was an issue or whether we shouldn't be using msbuild /m to enable concurrent builds.
[11:45:48] [BuildSolution] 17>MSBUILD : warning : WARN [09/02/16 1:45:48:65] Could not determine assembly version: LibGit2Sharp.LockedFileException: The index is locked. This might be due to a concurrent or crashed process [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Core.Ensure.HandleError(Int32 result) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Core.Proxy.git_checkout_tree(RepositoryHandle repo, ObjectId treeId, GitCheckoutOpts& opts) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Repository.CheckoutTree(Tree tree, IList`1 paths, IConvertableToGitCheckoutOpts opts) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Repository.Checkout(Tree tree, CheckoutOptions checkoutOptions, String refLogHeadSpec) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Repository.Checkout(Branch branch, CheckoutOptions options) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at LibGit2Sharp.Repository.Checkout(String committishOrBranchSpec, CheckoutOptions options) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at GitTools.Git.GitRepositoryHelper.EnsureLocalBranchExistsForCurrentBranch(Repository repo, String currentBranch) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at GitTools.Git.GitRepositoryHelper.NormalizeGitDirectory(String gitDirectory, AuthenticationInfo authentication, Boolean noFetch, String currentBranch) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at GitVersion.GitPreparer.Initialise(Boolean normaliseGitDirectory, String currentBranch) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at GitVersion.ExecuteCore.ExecuteGitVersion(String targetUrl, String dynamicRepositoryLocation, Authentication authentication, String targetBranch, Boolean noFetch, String workingDirectory, String commitId, Config overrideConfig) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
[11:45:48] [BuildSolution] MSBUILD : warning : at GitVersion.ExecuteCore.TryGetVersion(String directory, VersionVariables& versionVariables, Boolean noFetch, Authentication authentication) [Y:\Work\refs\heads\master\source\Octopus.E2ETests\Octopus.E2ETests.csproj]
This occurs on TeamCity 10.0.0 building ~30 projects. We've only just upgraded to GitVersionTask.3.6.3 from GitVersionTask.3.6.1 which had the gitversion_cache issue.
In the meantime I'll try some more builds and see if it recurs frequently.
The very next build through TeamCity failed with the exact same message and stack trace.
I'm going to revert to msbuild without the /m for now.
Id switch to the exe, run it with /updateAssemblyInfo before building.
We would have to grab a mutex, and block builds until the first task has finished calculating the version then unblock the others and they get the version from the cache.
Perhaps we could use .git/index.lock for this? I believe it would be semantically correct to lock the entire Git repository for all and any change meanwhile we calculate a version. If .git/index.lock exists, we could await version calculation until it's deleted or within a configurable time-out.
Im happy with that
any updates here?
@bbowman: I'm sorry, but nothing has happened besides agreeing on a solution that is yet to be implemented. We need someone in the team to have time to implement this, or get someone else to submit a PR. I unfortunately have my hands full. Perhaps you or @michaelnoonan can submit a PR?
any update on this? builds take 4x as long without parallelism :(
@jnevins-gcm: Sorry, there's nothing new to report on this. If you are able to provide a pull request that fixes this as described in https://github.com/GitTools/GitVersion/issues/1031#issuecomment-244749460, it will be merged quickly provided it passes all tests.
Thanks for the update. Even if I fix, how would I manage to get that into GitVersionTask?
Is there any short term workaround I could do with retries?
@jnevins-gcm, if you submit a pull request to this repository, it will be merged and a new package of GitVersionTask will show up on NuGet that you can use.
Now that we've upgraded to LibGit2Sharp version 0.26 in #1713, I hope this problem might be fixed. Please test with the latest v5 beta and reopen if the problem persists.
Hi,
We are having the same issue using the latest beta version (5.0.0-beta5.11).
Also hitting this issue on linux and Windows (but not MacOS) build agents using GitVersion 5.0.1
@StevenDevooght and @borrillis, can you please tell me more about your build environments that might help us figure out why this is happening?
@asbjornu This is the repo and branch I am currently testing with: https://github.com/axiom3d/axiom/tree/azure-pipelines and I am only seeing it fail in the CI/CD builds in Azure DevOps (https://dev.azure.com/axiom3d/Axiom/_build/results?buildId=37&view=results), locally it is working using the command .\build.ps1 -Script build.cake -Target Build -Configuration Release -Verbosity Diagnostic --settings_skipverification=true
@StevenDevooght, are you also seeing this on Azure DevOps?
@asbjornu, We have been having the same issue in our CI/CD builds on our private GitLab instance
We also have the issue when building on Azure DevOps. Locally we haven't experienced any issues.
Also see it in Azure Dev Ops, intermittently. Never happens when we run the cake build locally though.
We also have this issue.
dotnet core 2.2.203
solution with a dozen or so projects
Often it's "build->fail, build->fail, build->fail, build->succeed". Doesn't ever seem to happen locally when building through VS. We've tried adding "-p:maxCpuCount=1" to the build command to see if we could force it to build without parallelization (thinking that might have something to do with it), but no dice.
edit: also this doesn't just happen on build, it also sometimes happens on "dotnet test" as well.
@dieterv, since you're able to reproduce this in a local GitLab instance, we might have a chance to figure out why this is occurring. Are there any parallelization going on in your GitLab build that might cause this?
@asbjornu, I'm operating a single Gitlab build runner that accepts a single build job at a time (concurrent=1 in the config.toml file). Our CI pipeline in question runs a cake build script. The cake script runs a sequence of setup>clean>restore>validate>build>test>package>publish tasks.
The build task executes msbuild without /m parameter and with /nodeReuse:false parameters.
I've been experimenting with our setup these last couple of weeks and seem to have found something interesting. Since these changes we have seen 40 builds succeed without encountering the issue. Either we're simply extremely lucky or one or a combination of these changes "fixed" it for us. I was going to wait a bit longer to be sure, but well, maybe this information can help...
Our cake script was executing GitVersion.exe /output json and stored the output for later use in the setup task. That is before msbuild was executed in the build task. I've switched the order around and now only ever execute GitVersion.exe after the build succeeds in the publish task.
Without exception, all our projects contained a package reference to GitVersionTask. I've removed the reference for our xunit test projects (output type=class library)
I simplified the gitversion.yml file a bit. No idea why it had grown so complex. I now have:
mode: ContinuousDeployment
branches:
master:
tag: master
increment: Patch
prevent-increment-of-merged-branch-version: true
track-merge-target: false
release:
tag: beta
increment: Patch
prevent-increment-of-merged-branch-version: true
track-merge-target: false
feature:
tag: useBranchName
increment: Inherit
prevent-increment-of-merged-branch-version: true
track-merge-target: false
ignore:
sha: []
Before we had:
diff --git a/gitversion.yml b/gitversion.yml
index 11204d4c..4bacb5b4 100644
--- a/gitversion.yml
+++ b/gitversion.yml
@@ -1,32 +1,19 @@
mode: ContinuousDeployment
-tag-prefix: '[vV]'
-continuous-delivery-fallback-tag: ci
-major-version-bump-message: '\+semver:\s?(breaking|major)'
-minor-version-bump-message: '\+semver:\s?(feature|minor)'
-patch-version-bump-message: '\+semver:\s?(fix|patch)'
-no-bump-message: '\+semver:\s?(none|skip)'
-legacy-semver-padding: 4
-build-metadata-padding: 4
-commits-since-version-source-padding: 4
-commit-message-incrementing: Enabled
branches:
master:
- mode: ContinuousDeployment
tag: master
increment: Patch
prevent-increment-of-merged-branch-version: true
track-merge-target: false
release:
- mode: ContinuousDeployment
tag: beta increment: Patch prevent-increment-of-merged-branch-version: true track-merge-target: false feature: - mode: ContinuousDeployment tag: useBranchName increment: Inherit - prevent-increment-of-merged-branch-version: false + prevent-increment-of-merged-branch-version: true track-merge-target: false ignore: sha: []
Hope this helps!
One thing I'm trying as a workaround right now (Azure DevOps Pipeline) is to do this:
#Removes GitVersionTask from all csproj files before we build
Get-ChildItem .\*.csproj -Recurse | % {
[Xml]$xml = Get-Content $_.FullName
$xml | Select-Xml -XPath '//*[@Include="GitVersionTask"]' | ForEach-Object{$_.Node.ParentNode.RemoveChild($_.Node)}
$xml.Save($_.FullName)
}
/p:Version="$(GitVersion.AssemblySemVer)" /p:FileVersion="$(GitVersion.AssemblySemFileVer)" /p:InformationalVersion="$(GitVersion.InformationalVersion)" /p:PackageVersion="$(GitVersion.NugetVersion)"Since we don't use AssemblyInfo.cs at all (dotnet core SDK), this seems to work. Whether it will fix the issue is unknown since it's wildly unpredictable as to when it happens.
edit: added the powershell script I used.
One thing I'm trying as a workaround right now (Azure DevOps Pipeline) is to do this:
- Run a powershell script that will remove the node for GitVersionTask from csproj for all the projects. This means that it won't use the msbuild functionality at all.
#Removes GitVersionTask from all csproj files before we build Get-ChildItem .\*.csproj -Recurse | % { [Xml]$xml = Get-Content $_.FullName $xml | Select-Xml -XPath '//*[@Include="GitVersionTask"]' | ForEach-Object{$_.Node.ParentNode.RemoveChild($_.Node)} $xml.Save($_.FullName) }
- Added a GitVersionTask execution in the pipeline which runs and sets the various version strings as pipeline variables
- Update the dotnet build step to include several arguments which sets the versions from step 2. That looks like this:
/p:Version="$(GitVersion.AssemblySemVer)" /p:FileVersion="$(GitVersion.AssemblySemFileVer)" /p:InformationalVersion="$(GitVersion.InformationalVersion)" /p:PackageVersion="$(GitVersion.NugetVersion)"Since we don't use AssemblyInfo.cs at all (dotnet core SDK), this seems to work. Whether it will fix the issue is unknown since it's wildly unpredictable as to when it happens.
edit: added the powershell script I used.
This is working for us - the only annoying thing is that package version does not contain the full semver for commits ahead in the same way the gitversion package does. i.e. 1.9.0-alpha0123 Vs. 1.9.0-alpha.122.
Much like the others, we're seeing this using on Azure DevOps, using the .Net Core build step. We're building a solution with 19 projects, of which around 16 have the gitversion NuGet package.
edit: using "Automatic package versioning" option on the .Net Core task with the Pack option, we can set the Environment Variable option to be GITVERSION_FullSemVer. This is working well.
We're experiencing this too on a local GitLab instance when building solutions which contain multiple projects that reference GitVersionTask.
Put the following in each project file (or a Directory.build.props file in the solution dir):
<PropertyGroup Condition=" '$(GitVersion_SemVer)' != ''">
<GetVersion>false</GetVersion>
<WriteVersionInfoToBuildLog>false</WriteVersionInfoToBuildLog>
<UpdateAssemblyInfo>false</UpdateAssemblyInfo>
<Version>$(GitVersion_FullSemVer)</Version>
<VersionPrefix>$(GitVersion_MajorMinorPatch)</VersionPrefix>
<VersionSuffix Condition=" '$(UseFullSemVerForNuGet)' == 'false' ">$(GitVersion_NuGetPreReleaseTag)</VersionSuffix>
<VersionSuffix Condition=" '$(UseFullSemVerForNuGet)' == 'true' ">$(GitVersion_PreReleaseTag)</VersionSuffix>
<PackageVersion Condition=" '$(UseFullSemVerForNuGet)' == 'false' ">$(GitVersion_NuGetVersion)</PackageVersion>
<PackageVersion Condition=" '$(UseFullSemVerForNuGet)' == 'true' ">$(GitVersion_FullSemVer)</PackageVersion>
<InformationalVersion Condition=" '$(InformationalVersion)' == '' ">$(GitVersion_InformationalVersion)</InformationalVersion>
<AssemblyVersion Condition=" '$(AssemblyVersion)' == '' ">$(GitVersion_AssemblySemVer)</AssemblyVersion>
<FileVersion Condition=" '$(FileVersion)' == '' ">$(GitVersion_AssemblySemFileVer)</FileVersion>
<RepositoryBranch Condition=" '$(RepositoryBranch)' == '' ">$(GitVersion_BranchName)</RepositoryBranch>
<RepositoryCommit Condition=" '$(RepositoryCommit)' == '' ">$(GitVersion_Sha)</RepositoryCommit>
</PropertyGroup>
</Project>
Then run gitversion before executing the build and write the output into environment variables. This way gitversion is only run once and the result is then reused in each project being built.
gitversion /output json | Out-String | ConvertFrom-Json | ForEach-Object { foreach ($item in $_.PSObject.properties) { Set-Item -Path "env:GitVersion_$($item.Name)" -Value $item.Value } }
You also need to set the version properties (which is normally performed within the GetVersion task here) and disable other tasks like WriteVersionInfoToBuildLog.
Just as a followup. The approach I posted back in October has been running fine since. I had to do a little bit of adjustments to the way the GitVersion.yml was setup because it seemed to behave just slightly differently than when using the msbuild way, but otherwise it's been running without issue since. So at least now things seem stable and reliable. Having to only run gitversion once at the beginning seems to be the way to go.
During my inquiries I realized that each task is executing gitVersionCalculator.CalculateVersionVariables() on its own, which tries to access the repository (and probably fails with the afforementioned error) before it even checks if a cache or a gitversion.properties file exists.
I'd suggest to try to restructure the build tasks, so that:
gitVersionCalculator.CalculateVersionVariables(). All the other tasks depend on its output.There is a single task in the chain which actually runs
gitVersionCalculator.CalculateVersionVariables(). All the other tasks depend on its output.
I like the sound of this, @rose-a. Would you be up for providing a pull request for such a reorganization?
It should be possible to enable the use of the gitversion.properties file most of the build-server extensions generate for populating the GitVersion variables
This sounds a lot like what is proposed in #983. It's been open almost 4 years, so if someone finally has the time and energy to tackle that, it would be awesome. 馃槄
Tangentially related: #1227
Just for Info about the removing of GitVersion from the Project File. If you set the Build Property DisableGitVersionTask to true, you can have the same result without modify the project file.
Furthermore, it does not work at all with Jenkins (and probably others for similar reasons). The only 100% working setup is using the aforementioned workaround with _Directory.build.props_ file, gitversion executable, _gitversion.properties_, and environment variables injected during the build process.
Calling _dotnet build_ works only outside of Jenkins, i. e. in any command-line available. Visual Studio works, too. GitVersionTask fails in Jenkins with _LibGit2Sharp.LockedFileException_, while _GitVersion.exe_ behaves as expected and works everywhere.
dotnet publish SolutionName.sln -c release -r win10-x64
The example build command works only outside of Jenkins (copy-paste with no changes).
Adding /p:WriteVersionInfoToBuildLog=false fixes the problem for me, somehow.
Just in case anyone needs gitversion before the errors are corrected:
assembly: AssemblyVersion, AssemblyFileVersion, and AssemblyInformationalVersion from the AssemblyInfo.cs files. GitVersion.yml inside the solution directory.Path environment variable.GitVersionTask NuGet package to all project files. It will be used by Visual Studio, etc.Execute Windows Batch Command frame, modify if necessary and/or replace with your own build commands:gitversion /updateassemblyinfodotnet publish SolutionName.sln -c release -r win-x86 /p:DisableGitVersionTask=trueIt works for build servers if we disable MSBuild task via the property, and inject the version number directly to each AssemblyInfo.cs file. Everywhere else it will just use GitVersionTask if not specified otherwise. Simple and reliable.
@XadE-Blue p:DisableGitVersionTask much cleaner then using the powershell script, cheers for that.
This issue aside, is it advised to always disable GitVersion per project anyway on a build machine and inject the Git Version Build Task centrally generated version details? Otherwise you're running GitVersion per project, calculating the version per project, which takes considerable extra time when you've got a Solution of 20 projects (all producing a NuGet package as part of a dependent chain).
same, issue on AzureDevOps it works with version 5.0.1 and doesnt work with version 5.1.3
we need to try this one https://github.com/microsoft/msbuild/pull/4785
@arturcic I suppose that still runs the GitVersion tasks simultaneously for all projects in the solution...
I guess this is on the solution level only, and they are injected once. Would be great if someone could give it a try to confirm/or not the assumption.
I am currently working on a solution as I stated in a previous comment, but I deleted it, because it was crushed thoughts. My solution caches the result from GitVersion identifiable by key. So this key you have to provide before as msbuild commandline property and all subsequent builds with this key will be able to use the cache instead of recalculate the git informations. The work is in progress.
Ahhh and at least, I am not sure 'till now, if my solution will work like an add-on, so you need GitVersion and my solution as dependency, or if it will wrap the hole GitVersionTask package, so you only will need to install my solution. The GitVersionTask.targets is not that extendible as I wish. 馃槃 (perhaps you would agree to an pull request? 鉂わ笍 )
Edit: If not obvious: the habitat of my solution is in msbuild, so I won't extend the functionality of GitVersionTask itself, because the guys from GitVersion decided to make the GitVersionTask pretty internal/private/secret.
I just started, so is it worth to implement a cache at msbuild level? As I have seen with one eye, GitVersion would make a libgit call, to check for cache update, but there it is already too late. We know that when we do a build with lots of projects, that within the project builds the informations GitVersion does provide won't change, so cache at msbuild level could be cache at build level and not at git level. So what do you think?
@teroneko The problem is that builds can happen concurrently. So you can't guarantee that at least 1 build will have gotten through the pre-build step of running GitVersionTask before another build in the solution also tries to access that same information.
You'd almost need to modify the task to check if it's already running, and wait without error for that to complete and then check the cache I'd think. (just theorizing right now)
@iamwyza I would pick up at your last sentence: yes, builds can be concurrent, but my implementation idea is that the cache and the check for its existence, will happen before the routines of GitVersionTask are running at all, furthermore in my implementation I will disable the MSBuild targets of GitVersionTask right after either I've created the cache (DependsOnTargets="GetVersion") or it has been fetched. Instead of the default targets GetVersion, UpdateAssemblyInfo and so on, I call targets that do the same as the targets named before, with the one exception, that I use for each target a custom MSBuild task with the logic of original counterpart but with a custom implemented IGitVersionCalculator and inject it when I grab for their ServiceProvider from BuildServiceProvider (assembly=GitVersion.MSBuildTask, file=GitVersionTasks). Whether or wether not the cache does exist I decide in my custom implemented MSBuild task. I hope you can follow me. If not, just ask.
Edit: Ahh and of course you would need to install my solution to every project where you would GitVersionTask have installed. And second: The cache implementation will be based on parent GitVersion.yml location.
The general idea seems good, but customizing all of them would not be stellar. Ideally there'd just be a pre-solution build step that is executed that does nearly the same thing as gitversion task and writes that out to a file. It's very similar to what you're suggesting, but the general idea would be not to actually require any real changes to existing setups other than to add whatever is needed to tell it to run before any projects are compiled. So I think we are of like mind on this, but how we arrive there is different.
Yes I thought about it a lot, but when thinking about it, I found too many reasons against that pattern. The problem is, when you build any project with dependencies, first the dependencies and then the final build is built. So there is no target, no hook, before ALL projects: please do this and that, when you are hooking targets like the BeforeBuild or ResolveProjectReferences (or what's it called). So one solution would be to add a complete pre-solution step, but that's akward, when working in Visual Studio, where you want to see the same results like you would get from cake or a custom build script. Just my thoughts.
So yes the easiest for everyone is just to implement a custom cache, and inject PackageVersion and so on as msbuild properties to have them everywhere, but as I know you would not have a dynamic generated AssemblyInfo.cs in obj\ and therefore no branch+commit+... tagging when viewing your .dll-file by windows explorer for example, because GitVersionTask would be then disabled like most people recommended here.
If you check out my workaround above you'll notice that GitVersion is only disabled when there is a "GitVersion_SemVer" environment variable set.
This way it's only disabled on the build server which executes the powershell snipped I also shared before building the solution to generate the version info only once.
I've never experienced the concurrency problem when building from within Visual Studio, and since there is no environment var GitVersion_SemVer set in the local build environment, the GitVersionTasks are executed normally without any "tweaks".
I'd suggest optimizing things in a step-by-step way:
I personally would be happy already if we get to step 2, because running that powershell snippet (or just invoking gitversion somehow) in a ci build doesn't cost me anything.
Step 3 would be the icing on the cake, but I deem this to be the hardest (and probably the most "invasive") part of the solution.
You are right, I also do not face that problem when building in Visual Studio.
I think your solution is the right one from the CI-view, wouldn't do it different. I am creating such a tool for cache, because I would like to split the versioning in sub directories in the future (should be possible). And then you would do that customizing not only once but that often you have different GitVersion.yml files, or even more often for different CI's, because some are using DevOps, Appveyor and Travis simultaneously. I just want have something that I just have to drop-in and just works like a package install. A paraphrase would be, that I want to to keep a project as much as isolated from outside build process.
Like I told before, you solution is the right workaround for those who face that lock-file problem. 馃槉
@teroneko Wouldn't Step 1 and 2 also help you with your solution? I.e. just make the GetVersion taks aware of your chache or either use the build vars or a gitversion.props file as the cache?
I'd say those would be a big improvement from a design point of view regardless of what you actually plan to do with it ;)
The gitversion.props are based on git changes. How do I know them - I call over libgit, whether branch, commit or latest git tag (because it will be considered by GitVersion) are changed. I might interpreted it like that, while digging in the code, but I might be wrong. So that kind of cache is not multi project aware.
But the hole problem is the infrastructure:
The <UsingTasks>-statements are not conditionally in GitVersionTask.targets so no chance to override them.
The use of those GetVersion task call in the target GetVersion in GitVersionTask.targets, but use then the properties they have been set before by command line, Directory.Build.props or .csproj.
And then there would be the final problem, that the tasks itself which are finally implemented in GitVersionTask.dll+GitVersion.MsBuild.dll(+GitVersionCore.dll) are referring to IGitPreparer, which default implementation will definetely call libgit and does contain the git cache logic and provide finally VariableInformations SemVer, Sha, ShortSha, InformationVersion and so on which are used for example for the AssemblyVerision.cs (the task UpdateAssemblyInfo (in MSBuild) does not to input them by parameters, so the task (in assembly) will load them in internally) generation or the output for the variables which are used in GitVersion target in GitVersionTask.targets to define the package or the constants WiX Tools relevant variables.
That's the reason why I first load the GitVersion informations initially (BeforeTargets="BeforeBuild; GitVersion", because it is before everyting else where GitVersion put itself their targets before, namely CoreCompile, and so on) and save them with a build identifier, to let identify other projects with same identity key, that this cache has been already created. :)
GitVersion already has a cache mechanism and heuristic for generating a key that is unique for the current execution arguments and commit + branch combination. Couldn't that be used here as well?
Yeah, the main issue here is the concurrency problem which is caused by
GetVersion, WriteVersionInfoToBuildLog, UpdateAssemblyInfo, ...) accessing LibGit on their own to evaluate the version info, which highly increases the propability that multiple simultaneous builds access the repo at the same timegitVersionCalculator.CalculateVersionVariables() which is called by all those tasks does not try to access a cache before using LibGitTo be absolutely sure in a build server environment (and to save on resources) I'd actually prefer executing gitversion once and then be able to reuse that output (not just for the build itself).
I'm not quite sure if the cache thing can be nailed down perfectly to be safe for concurrent builds, since those happen in different processes...
Okay I see that GetRepositorySnapshotHash does access branch infos and so on, and I might be sure that this does use libgit!? That the problem like @rose-a just told: it is the concurrency problem, that all concurrent projects (intelligent chosen by msbuild 馃懠) are trying to access libgit. You should just implement your own lock mechamism. Just lock your GitVersion.yml (or unique temp file) around public GitVersionCacheKey Create(Config overrideConfig) or where ligbit is called too before task has the VariableInformations and it should be good!? Also there should be a retry mechanism when locked that you can enter the area after the lock ended in concurrent task call.
- all tasks (
GetVersion,WriteVersionInfoToBuildLog,UpdateAssemblyInfo, ...) accessing LibGit on their own to evaluate the version info, which highly increases the propability that multiple simultaneous builds access the repo at the same time- the method
gitVersionCalculator.CalculateVersionVariables()which is called by all those tasks does not try to access a cache before using LibGit
Good point, I guess we need to limit the code that uses Libgit2Sharp, and rely on cache, if possible
The concurrency problem with gitversiontask has been a known issue for a while.
I have not seen anyone mention this workaround yet.. Something I have been using is the
--disable-parallel flag when issuing any dotnet commands. Example: https://github.com/dazinator/DotNet.Glob/blob/develop/appveyor.yml#L20
When discussed in the past, I was a big fan of running GitVersion.exe once to prime the cache, and then make a change to GitVersionTask to check the cache first and use the values there before hitting the git repo. This change would fix this issue for those that already use that workflow. The next level we discussed was having the tasks lock - using some sort of lock file whilst accessing the git repo. I believe both these changes would be very welcome PR's?
@arturcic has added some more test coverage for GitVersionTask recently, so perhaps now is a good time to add these changes!
@arturcic it would be interesting to see if we can add a check to repro this - by building a solution with multiple projects containing GitVersionTask in, instead of just the one!
@dazinator sure, I'll think about the way we can catch this
Should this be raised with LibGit2Sharp to make change to a blocking or R/W lock?
Still hitting this issue when referencing GitVersionTask from multiple projects (even with --disable-parallel)
https://github.com/isc30/blazor-lazy-loading/runs/677097147?check_suite_focus=true#step:6:125
Ideally GitVersionTask should execute once per build, not once per project. This could be accomplished pretty easily by relying on MSBuild target caching.
In the nupkg build targets:
<Project>
<PropertyGroup>
<GitVersionVersionPropertyName Condition="'$(GitVersionVersionPropertyName)' == ''">NuGetVersion</GitVersionVersionPropertyName>
<SetBuildServerVersion Condition="'$(SetBuildServerVersion)' == ''">true</SetBuildServerVersion>
<!-- Call GetVersion in order to emit the correct Package Version attribute to nuspec file for when Project References in a solution should be pulled in as Package References -->
<GetVersionBefore Condition="'$(GetVersionBefore)' == ''">DispatchToInnerBuilds;BeforeBuild;CoreBuild;GetAssemblyVersion;CoreCompile;GenerateNuspec;EnsureWixToolsetInstalled</GetVersionBefore>
</PropertyGroup>
<Target Name="GetVersion" BeforeTargets="$(GetVersionBefore)" Condition="'$(UseGitVersion)' == 'true'">
<PropertyGroup>
<GetGitVersionProperties>GitVersionVersion=$(GitVersionVersion);GitVersionVersionPropertyName=$(GitVersionVersionPropertyName);GitRepositoryPath=$(GitRepositoryPath);SetBuildServerVersion=$(SetBuildServerVersion);OverwriteGitVersionYml=$(OverwriteGitVersionYml)</GetGitVersionProperties>
</PropertyGroup>
<!-- Keep restore separate and include extra property to prevent caching (restore and other targets in same MSBuild call is not a scenario supported by MSBuild) -->
<MSBuild Projects="$(MSBuildThisFileDirectory)/GetGitVersion.proj" RemoveProperties="TargetFramework;TargetFrameworks" Targets="Restore" Properties="$(GetGitVersionProperties);Restoring=true" />
<MSBuild Projects="$(MSBuildThisFileDirectory)/GetGitVersion.proj" RemoveProperties="TargetFramework;TargetFrameworks" Targets="GetVersion" Properties="$(GetGitVersionProperties)">
<Output TaskParameter="TargetOutputs" ItemName="GitVersion" />
</MSBuild>
<PropertyGroup>
<Version>@(GitVersion)</Version>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup>
<Message Text="Setting version to $(Version)." Importance="high" />
</Target>
</Project>
and then have a proj file:
<Project>
<!-- Helper project that ensures single request to GitVersion per build -->
<PropertyGroup>
<!-- Use separate path so doesn't interfere when writing project assets json -->
<BaseIntermediateOutputPath>obj/GetVersion</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.Common.CurrentVersion.targets" />
<ItemGroup>
<PackageReference Include="GitVersion.CommandLine" Version="$(GitVersionVersion)" PrivateAssets="All" />
</ItemGroup>
<Target Name="Build" />
<Target Name="Rebuild" />
<Target Name="GetVersion" Returns="$(Version)" DependsOnTargets="GetGitVersion;SetBuildServerVersion" />
<Target Name="GetGitVersion">
<!-- project.assets.json is the best supported way of finding what version of GitVersion.CommandLine was resolved by the PackageReference -->
<Exec ConsoleToMSBuild="true" Command="powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -Command gc '$(ProjectAssetsFile)' ^| ConvertFrom-Json ^| Select -ExpandProperty libraries ^| gm -MemberType NoteProperty ^| Select -ExpandProperty Name -First 1">
<Output TaskParameter="ConsoleOutput" PropertyName="GitVersionCommandLinePath" />
</Exec>
<PropertyGroup>
<GitVersionCommandLinePath>$(NuGetPackageRoot)/$(GitVersionCommandLinePath)/tools/GitVersion.exe</GitVersionCommandLinePath>
</PropertyGroup>
<Error Text="Could not find GitVersion.exe at $(GitVersionCommandLinePath)" Condition="!Exists($(GitVersionCommandLinePath))" />
<Exec ConsoleToMSBuild="true" Command=""$(GitVersionCommandLinePath)" /nofetch /showvariable $(GitVersionVersionPropertyName) $(GitVersionSetBranch)" WorkingDirectory="$(GitRepositoryPath)">
<Output TaskParameter="ConsoleOutput" PropertyName="Version" />
</Exec>
<Message Text="Resolved version for repository at $(GitRepositoryPath) to $(Version) using GitVersion." Importance="high" />
</Target>
<Target Name="SetBuildServerVersion" Condition="'$(SetBuildServerVersion)' == 'true'">
<Exec Command=""$(GitVersionCommandLinePath)" /nofetch /output buildserver $(GitVersionSetBranch)" WorkingDirectory="$(GitRepositoryPath)" />
</Target>
</Project>
which could alternatively just call the MSBuild task dll...
bump...
this issue makes gitversiontask pretty useless
Agreed, this is unusable when creating packages for multiple projects.
The workaround worked for me (disable parallel builds). There have been longer term discussions to fix this. If you want to see this issue fixed for GitVersionTask in the near term it needs someone to contribute a fix, and get involved. Or don't and wait on someone else to fix it. The long term discussion I was aware of involve :
Adding test coverage to detect this issue during CI builds. That way we can make that test / check active on a branch and verify the fix there also.
Talk of using our own lock around git operations. Not sure if this had legs.
Also just in case it helps, I have started removing GitVersionTask from my projects. In my case I have found running gitversion.exe at the start of my CI builds, and then when doing dotnet build or pack, pass the version variables as msbuild properties into those commands achieves the same thing and confines the versioning logic to the CI server.
.. and here is an example of what I mean: https://github.com/dazinator/DotNet.Glob/blob/develop/appveyor.yml
I'll put this out there once more - I think this is a good solution and is how I'm working around this bug in our builds...I can put into PR form if there is buy in here...
In build.targets:
<Target Name="GetVersion" BeforeTargets="DispatchToInnerBuilds;BeforeBuild;CoreBuild;GetAssemblyVersion;CoreCompile;GenerateNuspec;EnsureWixToolsetInstalled">
<!-- Keep restore separate and include extra property to prevent caching (restore and other targets in same MSBuild call is not a scenario supported by MSBuild) -->
<MSBuild Projects="$(MSBuildThisFileDirectory)/GitVersion.proj_" Targets="Restore" Properties="SolutionName=$(SolutionName);Restoring=true" />
<MSBuild Projects="$(MSBuildThisFileDirectory)/GitVersion.proj_" Targets="Build" Properties="SolutionName=$(SolutionName)">
<Output TaskParameter="TargetOutputs" PropertyName="Version" />
</MSBuild>
<PropertyGroup>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup>
<Message Text="Setting version to $(Version)" Importance="high" />
</Target>
and move the versioning into a separate project (GitVersion.proj_)
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>$(Temp)/$(SolutionName)/GitVersion/obj</BaseIntermediateOutputPath>
<OutputPath>$(Temp)/$(SolutionName)/GitVersion/bin</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)/Microsoft.Common.CurrentVersion.targets" />
<Import Project="$(BaseIntermediateOutputPath)/$(MSBuildThisFileName)$(MSBuildThisFileExtension).nuget.g.targets" Condition="Exists('$(BaseIntermediateOutputPath)/$(MSBuildThisFileName)$(MSBuildThisFileExtension).nuget.g.targets')" />
<ItemGroup>
<PackageReference Include="GitVersionTask" Version="5.3.3" />
</ItemGroup>
<Target Name="Build" DependsOnTargets="GetVersion;WriteVersionInfoToBuildLog" Returns="$(GitVersion_NuGetVersion)" />
</Project>
MSBuild target caching will ensure GetVersion will only be called once per build of the entire solution.
@admin-simeon thanks for this solution. I found some issues with it, maybe we can work together to get a fully working workaround:
<GeneratePackageOnBuild>, the nuget package doesn't contain subdependencies with the proper version (they are 0.0.0)Build or Restore, which makes it hard to fix some of these issues@isc30
My GitVersion.yml is taken into consideration - it is in the same directory as the sln file/the root of the git repository. Do you have GitVersion.proj_ somewhere underneath your Git repo or is it somewhere else? That could be the difference
GeneratePackageOnBuild with subdependencies - good find...does this not happen with the default task? I would assume this is because Pack isn't triggering versioning of the referenced projects...
What do you mean by "it can't run"?
Somehow I feel sad about GitVersionTask. It seems not thought to the end. It does not take into account a multi project solution and even less it is built for concurrency. Why do you rely on locking machanism of LibGit2Sharp? It does not seems to work. So just don't rely on it. Caching is nice, but as long you need make lookup git-metadata for each call to GetVersion to look for such a cache on MSBuild-level (you call GetVersion each time), you will run into these lock exception at some time.
Currently I am working on an add-in, that just disables the task GetVersion of the package GitVersionTask and does this stuff by itself with the help of GitVersion.exe. What's the reason you not using GitVersion.exe by yourself in GitVersionTask(.MSBuild) (@asbjornu)? GitVesionTask could carry it on by itself and could replace the current implementation of the task GetVersion with some clean lines of code. In such an implementation it would be easy to implement a global lock mechanism and even to implement a kind of global identifier for caching purposes of solution with multi projects that rely heavily on GitVersionTask.
I'm sad about the current state of GitVersionTask too, @teroneko. Parallelism is something Microsoft introduced as default in later versions of .NET, which is why the issue with locking is "new". It has always existed, though.
I think the proper solution to this problem may come with version 6 of GitVersion (see #2275) , where we modularize and separate things so a build can be created from a sequence of steps instead of just a single GitVersion step as is the current way of operation. A suggested way to do this in version 6 would be:
gitversion calculate and store the version variables in gitversion.jsongitversion.json instead of doing its own calculation.dotnet build.I think this should work and that it is doable. The only optimization I would like to do, if possible, is to add some sort of hook into dotnet build so step 1 and 3 can be merged (i.e. dotnet build executes gitversion calculate before compilation starts).
Fixing this in version 5 would require someone to submit a pull request that provides a working file locking implementation. Until such a PR exists, this issue is going to stay open, I'm afraid.
As I am in working for an add-in that also allows nested GitVersion.yml's, but same .git-folder, I would agree to contribute a proper locking implementation. I see two options: Mutex or a file lock with a simple "sleep-and-retry-until-lock-release"-implementation. I think I will use last option.
May I ask you, if your interpretation of dotnet build belongs to a solution or a set of projects, or only to a single project, like GitVersionTask is designed? Because then, you are going to shift logic from GitVersionTask.MSBuild to GitVersion(Core), for modularity and simplicity right?
I just want to give you here three scenarios in which GitVersionTask is necessary.
; in dotnet msbuild -t:build or multiple projects with one or many --project= in dotnet build), or with cake/custom build project that also all belong to the same .git-folder. GitVersionTask should be involed in each project (see 2.)As you can see, it is necessary, that each project should have a package reference to GitVersionTask for its independency. To have the calculation and such logic like described (scope resolving and scoped caching) above in one product, let call it GitVersion.exe, would be great.
I red your proposal, and I think you want GitVersionTask behave the same like before, but with just moved logic like I described above, right?
As I am in working for an add-in that also allows nested GitVersion.yml's, but same .git-folder, I would agree to contribute a proper locking implementation.
馃檹 馃憦 馃檹 馃憦 馃檹
I see two options: Mutex or a file lock with a simple "sleep-and-retry-until-lock-release"-implementation. I think I will use last option.
As long as the method chosen is cross-platform, any method will do.
May I ask you, if your interpretation of dotnet build belongs to a solution or a set of projects, or only to a single project, like GitVersionTask is designed?
In version 6 we are moving towards executing gitversion calculate once at the start of the build, and then doing all the various transformations, outputs, code generation, etc., as individual steps that may occur once per project, once per AssemblyInfo file, etc. after.
Because then, you are going to shift logic from GitVersionTask.MSBuild to GitVersion(Core), for modularity and simplicity right?
Yes, absolutely.
As you can see, it is necessary, that each project should have a package reference to GitVersionTask for its independency. To have the calculation and such logic like described (scope resolving and scoped caching) above in one product, let call it GitVersion.exe, would be great.
That's the plan. Each GitVersionTask instance don't need to calculate their own version number, as the result will be the same each and every time.
I red your proposal, and I think you want GitVersionTask behave the same like before, but with just moved logic like I described above, right?
Yes.
I have implemented a simple and generic lock file utility. My image of current GitVersion implementation is, that each executable project implements his own concrete logic to grab the native binaries to be able to work with GitVersionCore.
So my question to you is, where you want to put me the lock? And another question: what can I lock?
Locking on .git/index.lock or any other file provided by git/LibGit2Sharp is not desirable, because you don't know if and when git or LibGit2Sharp are locking them. We should avoid any kind of possible nested locks.
At GitVersionCore level, it would be possible to lock on GitVersion.yml, and work with the filestream you get from the acquired lock. When finished with one action towards .git/gitversion_cache/ and GitVersion.yml that typically involves LibGit2Sharp, we can release lock on GitVersion.yml. What do you mean?
By the way here is the code related to file locking I want to contribute:
```C#
public static class LockFileTools
{
public const FileMode DefaultFileMode = FileMode.OpenOrCreate;
public const FileAccess DefaultFileAccess = FileAccess.ReadWrite;
public const FileShare DefaultFileShare = FileShare.None;
public static bool TryAcquire(string filePath, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare)
{
filePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
try {
fileStream = File.Open(filePath, fileMode, fileAccess, fileShare);
return true;
} catch (Exception error) when (error.GetType() == typeof(IOException)) {
fileStream = null;
return false;
}
}
public static bool WaitUntilAcquired(string filePath, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, int timeoutMilliseconds = Timeout.Infinite)
{
FileStream spinningFileStream = null;
var spinHasBeenFinished = SpinWait.SpinUntil(() => {
return TryAcquire(filePath, out spinningFileStream, fileMode: fileMode, fileAccess: fileAccess, fileShare: fileShare);
}, timeoutMilliseconds);
if (spinHasBeenFinished) {
fileStream = spinningFileStream ?? throw new ArgumentNullException(nameof(spinningFileStream));
return true;
} else {
fileStream = null;
return false;
}
}
public static bool WaitUntilAcquired(string filePath, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, TimeSpan? timeout = null)
{
timeout = timeout ?? Timeout.InfiniteTimeSpan;
var timeoutInMilliseconds = Convert.ToInt32(timeout.Value.TotalMilliseconds);
return WaitUntilAcquired(filePath, out fileStream, fileMode: fileMode, fileAccess: fileAccess, fileShare: fileShare, timeoutMilliseconds: timeoutInMilliseconds);
}
}
```
@teroneko, that's awesome! Would it be possible to put a lock on the cache file GitVersion already stores within the .git/gitversion_cache/ directory, or on the entire .git/gitversion_cache/ directory itself?
I'd also like to see a non-static implementation of the LockFileTools where repeating arguments like filePath is being passed once via a public constructor. I'm also not a huge fan of words like Tools, Utility, etc. FileLocker is better, imho. And perhaps if we move all of this code closer in dependence to the GitVersionCache class, it doesn't even need to be public?
It is just the abstraction, and are we really talking about the name? If you want read that below. About your question, it would be somehow possible to lock the folder, but it seems not very straightforward, so we should keep on file locking. Please help me, is it correct, that you associate a cache by meta data from git? Because then you couldn't lock on cache before you didn't got the data from .git with LibGit2Sharp. So a simple .git/gitversion_cache/gitversion.lock would do it too. You have to rely on the fact, that only you are using this directory, so why worry about any behaviour that can interrupt you. Because then you would need to do a lot more to ensure a "atomic" action for access git and cache to prevent any influence from outside.
My view of this is: You always have first an abstraction of something, then you are going to concrete something. In this case you have a generic file lock mechanism, somewhere written now to LockFileTools. You can call it whatever you want like I_Can_Lock_A_File. Tools refers for me a kind of static construction that applies on something. In this case: Lock(ing a )File. As the string is too generic to be an extension, Because of this, I decided to enclose it to the topic Lock(ing a )File. Now based on code, it will will happen to craft a class, for DI, or whatever, to wrap the static functions to reduce arguments of course and increase encapsulation and complexity to serve its actual purpose.
In my opinion is the privatization of code in this project a huge problem. For example I ask myself why you decided to make GitVersionTasks.BuildServiceProvider private. It is the core to make extendibility possible from outside, by providing own implementation of IGitVersionTaskExecutor. For my wondering, no one will ever touch it unless soemone references it by his self:
<Reference Include="$(PkgGitVersionTask)\tools\netstandard2.0\GitVersionCore.dll">
<Private>false</Private>
</Reference>
<Reference Include="$(PkgGitVersionTask)\tools\netstandard2.0\GitVersionTask.MsBuild.dll">
<Private>false</Private>
</Reference>
Of course, you are right. Yes, the cache file requires some information from LibGit2Sharp, specifically the SHA of the HEAD commit.
Preferably, we would have a .git/gitversion/ folder in which we could create a lock file alongside a cache folder with cache files in it. Changing the name of gitversion_cache could be seen as a breaking change, though, so we should probably not change that until version 6.
My distaste for Tool and Utility stems from an architectural standpoint where such classes often end up being kitchen sink classes that have no real purpos, break all the SOLID principles and becomes impossible to both test and maintain over time. They are like magnets for bad practices. Not that it's impossible to uphold the Single Responsibility Principle in a class called Tool, just that it requires more thought and clear sight by the developer than classes named more succinctly.
In my opinion is the privatization of code in this project a huge problem. For example I ask myself why you decided to make GitVersionTasks.BuildServiceProvider private. It is the core to make extendibility possible from outside, by providing own implementation of IGitVersionTaskExecutor.
Even though we provide a GitVersion.Core assembly, none of the classes exposed by GitVersion are written in a way that promotes external usage. It is all there just to serve GitVersion itself. If someone are able to use GitVersion as a library, that is despite of GitVersion's design, not because of it.
The IOC work laid down by @arturcic is, however, a step in the direction of making GitVersion easier to consume as a library. So it is on the roadmap, although not a high-priority goal. With version 6, I think we might be closer and during the refactorings we are going to do over the lifetime of version 6, I hope we might be in a good place by version 7, where GitVersion should have its own core domain model independent of LibGit2Sharp and where all implementations of everything infrastructure related can be injected through the DI container.
Thanks for your answering. When I find time, I will try to look around the code in GitVersionCode and seek for a possiblity to wrap each single action that involves GitVersion.yml, caching or LibGit2Sharp by an file lock. Another approach would be to lock on startup and unlock on exit. But that is quick and dirty and reminds me of the time, where you instantiated one SqlConnection during lifetime. :smiley:
Yes would become a breaking change if someone externals is relying on exact these created caches. But not sure if someone is really working with those temporary cach files that are generated by GitVersion and not by someone elses. I won't change any structure.
I think I have now all infos to proceed when I find time. I hope for weekend or next week.
One question, what lets me open, is a test case: It is quite difficult to create such an environment to test it. My project with its 10+ sub projects that relies on GitVersionTask could serve that kind of test, but it does fail only each second or third time due to LibGit2Sharp.LockedFileException.
What is your idea to make a test case? Maybe a kind of penetration test of a huge project with many sub projects someone can provide us?
Regarding to your distate of Tool and Utility. I can understand you and agree with you, that such a Tool whatever static prupose it does serve, can be lost quickly. But a library is not a library without such functions. Those functions cannot be related to only one purpose easily, So when you have two classes that serves completely its own purposes, but have both one method that share a bit of the same logic like Directory.Exists or File.Exists, then you should outsource them definetely to let them become Directory.Exists and File.Exists. My taste for Tools (or Utilities) are coming for this reason, that I don't want to collide with existing classes from System.*, but want to state its relation to them. So the name LockFileTools comes more or less by habit. When I think more over it: FileLocker does associate a kind of encapsulation between these static functions, so even when I would call it FileLocker: The class itself does not provide a file locker, but each of its static functions. Moreover they do not provide a file locker instead they do lock a file it without an instanstiated context so the name would irritate somehow. But this would change if FileLocker would be instantiable and would require a file path and provides methods like "Lock", then the class itself would be a file locker and therefore named FileLocker. Haha, but I do respect your programming style. 鈽猴笍
To your last words: I am looking forward that this library will become a strong library. 馃槉
Thanks for your answering. When I find time, I will try to look around the code in GitVersionCode and seek for a possiblity to wrap each single action that involves GitVersion.yml, caching or LibGit2Sharp by an file lock.
Sounds great! 馃憤
Another approach would be to lock on startup and unlock on exit. But that is quick and dirty and reminds me of the time, where you instantiated one SqlConnection during lifetime. 馃槂
It's a viable choice if other options are too difficult. It's much better than crashing and will still yield better performance with parallelization than a serial build would.
Yes would become a breaking change if someone externals is relying on exact these created caches. But not sure if someone is really working with those temporary cach files that are generated by GitVersion and not by someone elses.
I don't know either.
I won't change any structure.
Good. We can instead harmonize this a bit in v6 and gather everything GitVersion related into a .git/gitversion/ folder.
I think I have now all infos to proceed when I find time. I hope for weekend or next week.
Awesome!
What is your idea to make a test case? Maybe a kind of penetration test of a huge project with many sub projects someone can provide us?
I think we should be able to surface this bug with enough projects in a single solution, so a very simple repository with just a bare minimum solution with perhaps 100 projects all using GitVersionTask should be able to consistently reproduce this bug, I'd assume.
I've set up and invited you to GitVersion.TestCases so we can collaborate on the reproduction there. I'm thinking this one repository can have one branch per test case, starting with this parallelization problem.
But this would change if FileLocker would be instantiable and would require a file path and provides methods like "Lock", then the class itself would be a file locker and therefore named FileLocker.
My point exactly! I have a general distaste for static as well, since unless it's always paired with readonly and immutable objects, leads to global mutable state, meaning bugs and bad encapsulation. It's also impossible to mock in tests and generally doesn't play well IOC.
To your last words: I am looking forward that this library will become a strong library. 馃槉
So do I! 馃槂
Guys, the builds failing because of GitVersion creating *.lock files is a really annoying "feature". Any indication on when the PR "A lock file implementation applied to project GitVersionCore for resolving #1031" can be merged ? Looks like there was a work in progress abandoned in July :(
"
Most helpful comment
One thing I'm trying as a workaround right now (Azure DevOps Pipeline) is to do this:
/p:Version="$(GitVersion.AssemblySemVer)" /p:FileVersion="$(GitVersion.AssemblySemFileVer)" /p:InformationalVersion="$(GitVersion.InformationalVersion)" /p:PackageVersion="$(GitVersion.NugetVersion)"Since we don't use AssemblyInfo.cs at all (dotnet core SDK), this seems to work. Whether it will fix the issue is unknown since it's wildly unpredictable as to when it happens.
edit: added the powershell script I used.