LibraryManager
LibraryManager copied to clipboard
Errors running libman CLI commands as a pre-build step in projects building in parallel on an Azure hosted agent
Functional impact
Running into errors building projects in parallel on an Azure Pipelines hosted agent when they both invoke the libman CLI as a pre-build step.
Minimal repro steps
- Create a Visual Studio solution with 2 projects that both invoke "libman clean && libman restore" as a pre-build step
- Create an Azure Pipeline that
- Uses a 'dotnet' task to install the libman CLI to Agent.ToolsDirectory
dotnet tool install Microsoft.Web.LibraryManager.Cli --tool-path="$(Agent.ToolsDirectory)"
- Uses a 'Powershell' task that adds Agent.ToolsDirectory to the environment variables
Write-Host "##vso[task.setvariable variable=PATH;]${env:PATH};$(Agent.ToolsDirectory)";
- Builds the solution in parallel
- Uses a 'dotnet' task to install the libman CLI to Agent.ToolsDirectory
- Run the build on an Azure hosted agent
Expected result
Libman CLI commands run successfully
Actual result
Sometimes I see the following error output for one of the projects which causes the build to fail:
Unhandled Exception: System.TypeInitializationException: The type initializer for 'Microsoft.Web.LibraryManager.Configuration.Settings' threw an exception. ---> System.IO.IOException: The process cannot access the file 'C:\Users\VssAdministrator\.librarymanager\libman.config.json' because it is being used by another process.
Further technical details
I assume the issue is because both commands are running simultaneously and one puts a lock on libman.config.json so the other can't access it. I have run the build on a VM agent and do not see this error, though I installed libman globally on that machine so the 'install Libman CLI' step isn't part of the pipeline that runs on it.
Any chance you can get a callstack for the inner IOException? It seems like this could happen if they both try to create the file at the same time, since StreamWriter by default places a read lock when creating the file - i.e. if both tried to open the StreamWriter stream at the same time, maybe that's why we see this conflict?
If that is the case, one workaround would be to run any libman command earlier in the build (even libman --help
) as that would create the settings file. The default initialization code will try to read the file, and multiple concurrent readers should be fine if the file exists and is valid.