Using 'husky attach' inside multi-project solutions leads to compile errors due to 'husky install' running multiple times

See original GitHub issue

Version

v0.4.4

Details

After you run dotnet husky attach someproject.csproj, and doing so on multiple projects inside a solution to ensure people working on individual projects are forced to use Husky (in my case: formatting in pre-commit hook), leads to the following error:

The process cannot access the file 'D:\Projects\MyProject\.husky\_\.gitignore' because it is being used by another process.
error MSB3073: The command "dotnet husky install" exited with code 1.

It is clear that the error is caused by the parallel build feature of MSBuild, as each project in a solution is being build. I have not yet found a stable, different way of ensuring the dotnet husky install is run only once.

Hand-crafted ways that probably work is do a file-exists test of the .husky folder. But by itself, that is not enough, as Husky won’t run unless core.hooksPath of Git is set to .husky, which this command so conveniently does.

Ideally, dotnet husky install should not lock files it reads, or use a mutex of sorts (available cross-platform in dotnet nowadays) to prevent it from running multiple times.

Alternative, without using exclusive locks, dotnet husky install could just check if Husky is already installed and of the correct version. But that still leaves the issue that, if it is not installed yet, that “Rebuild All” will have multiple parallel dotnet husky install running.

Steps to reproduce

  • Create a solution with 2 or more projects (more is better)
  • Run dotnet husky attach for each project
  • Clean project, then Build All.

You will now get the above mentioned error.

PS: I really like Husky.Net. The above is just a minor inconvenience. After trying many different ways of creating cross-platform auto-installed pre-commit hooks for Git, basically nothing worked out of the box, but Husky did. It is awesome!!!

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
alirezanetcommented, Oct 9, 2022

Thanks for sharing your configuration, something like this could be an additional advanced feature to the husky attach command, I’ll be happy if you test this and share your final thoughts on another issue that you think is necessary for others facing a similar problem.

Btw, your name reminds me of this brilliant chess player, I assume you’re a different Alireza? ♟️

Yeah 😉, there are a lot of Alireza in Iran, but firoozja is originally from the city I’m living now. 😃 we have at least one thing in common 😅, plus I also like chess but my rating is half of his.

Have a nice day

1reaction
abelbraaksmacommented, Oct 9, 2022

To answer my own question, it turns out that Target Batching works. With your mutex (which prevents problems when run in parallel) and the Outputs vs Inputs behavior (where MSbuild diligently tests whether source has been updated), this indeed prevents the execution of the tasks.

Note that the current code still prevents unnecessary running of dotnet husky install if it is already installed. But tbh, the overhead of this is much less now that MSBuild takes care of dirty-flagging the installation (it only runs when the version is updated).

There’s now probably some code that’s redundant and hopefully a smaller version is possible. I’ll soon test with your mutexed version. For posterity, this is the current version (paths may need to be adjusted if people try to use this, i.e. like the path to dotnet-tools.json):

<Project>

  <PropertyGroup>
    <CurrentDate>$([System.DateTime]::Now.ToString(o))</CurrentDate>
  </PropertyGroup>

  <!-- 
    Prevent running Husky-Install when Target=Clean. 
    Must run before 'BeforeClean' (just before 'Clean' is not correct and will execute Husky anyway.)
  -->
  <Target Name="NoHuskyCheckOnClean" BeforeTargets="BeforeClean">
    <CreateProperty Value="1">
      <Output TaskParameter="Value" PropertyName="SkipHuskyCheck" />
    </CreateProperty>
  </Target>

  <!--
    This checks whether Husky is already installed, then installs it, if necessary.
  -->
  <Target Name="CheckAndInstallHusky" 
          BeforeTargets="Restore;CollectPackageReferences" 
          Condition="'$(SkipHuskyCheck)' != '1'" 
          Inputs="$(MSBuildProjectDirectory)\..\..\.config\dotnet-tools.json"
          Outputs="$(MSBuildProjectDirectory)\..\..\.config\husky-installed.lock">
    
    <Exec Command="git config --local --default &quot;&quot; --get core.hookspath" ConsoleToMSBuild="true" StandardOutputImportance="Low">
      <Output TaskParameter="ConsoleOutput" PropertyName="HuskyHookLocation" />
    </Exec>
    
    <CreateProperty Value="true" Condition="'$(HuskyHookLocation)'=='.husky'">
      <Output TaskParameter="Value" PropertyName="HuskyAlreadyInstalled" />
    </CreateProperty>

    <!-- restore dotnet tools -->
    <Exec Command="dotnet tool restore" StandardOutputImportance="Low" StandardErrorImportance="High" />
    <Message Condition="'$(HuskyAlreadyInstalled)'=='true'" Text="Husky installation skipped: already installed" Importance="high" />

    <!-- call husky installer conditionally -->
    <CallTarget Targets="InstallHusky" Condition="'$(HuskyAlreadyInstalled)'!='true'" />

    <!-- after successful task exec, ensure on next build it is not run again (MSBuild uses Inputs/Outputs compare for that) -->
    <WriteLinesToFile File="$(MSBuildProjectDirectory)\..\..\.config\husky-installed.lock"
                      WriteOnlyWhenDifferent="true"
                      Overwrite="true"
                      Lines="$(CurrentDate)"/>

  </Target>

  <!-- The actual Husky Install target, dependently called from CheckAndInstallHusky -->
  <Target Name="InstallHusky">
    <Message Text="Installing Husky" Importance="high" />
    <Exec Condition="'$(HuskyHookLocation)'!='.husky'" Command="dotnet husky install" StandardOutputImportance="Low" StandardErrorImportance="High" WorkingDirectory="..\.." />
  </Target>

</Project>
Read more comments on GitHub >

github_iconTop Results From Across the Web

npm install fails on husky installation · Issue #822
it fails multiple pipelines we have, and of course we don't maintain node:dubnium image so upgrading git is not possible. The only solution...
Read more >
How to fix error 'not found husky-run' when committing new ...
I checked the package.json and it has husky as a dependency, and I can see the pre-commit hook configuration for Husky in the...
Read more >
Using Husky Git Hooks and Lint-Staged With Nested Folders
The problem is, husky expects your package.json to be at the root of your project. That's a fine assumption to make a lot...
Read more >
Changelog | Finsemble
We've upgraded Finsemble's internal representation of apps from the FDC3 1.2 appD format to FDC3 2.0. Apps configured in the 1.2 format under...
Read more >
Untitled
Din 7983 form f, Amalfi coast italy in march, Double glazing lead designs, ... Two pulley system three masses, In new york song...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found