Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Husky setting wrong folder in monorepo (lerna/yarn workspace) #677

Closed
tad3j opened this issue Feb 21, 2020 · 34 comments
Closed

Husky setting wrong folder in monorepo (lerna/yarn workspace) #677

tad3j opened this issue Feb 21, 2020 · 34 comments
Labels

Comments

@tad3j
Copy link

tad3j commented Feb 21, 2020

Today I've noticed that Husky hooks suddenly stopped working in a monorepo. After investigating I've found out that file husky.local.sh inside .git\hooks had an entry on last line cd "packages/shared/". After removing this line Husky hooks started to work again.

My folder structure is like:
root:
-package.json (with Husky hooks setup)
-.git
--packages
---shared
-----package.json
---web
------package.json

Could this happen when I ran yarn add/install or lerna clean && lerna bootstrap from a sub-directory? Is Husky aware of lerna/yarn workspaces? One solution to always use the right directory would be to check package.json for husky key.

OS: Windows 10
Terminal: webstorm, CMD, cygwin
Since issue was pinpointed I'm not including command's outputs.

@bertdeblock
Copy link

I'm also using the same setup (Yarn Workspaces + Lerna) and I've noticed the exact same issue.

@yuanguandong
Copy link

i have the same issue

@melanieseltzer
Copy link

My setup is almost identical (using Yarn workspaces only, no Lerna) and I'm getting the same. This is what my structure looks like:

package.json (Husky setup at root)
- packages
  - client
    package.json
  - server
    package.json

I just did some testing and it's only happening with the yarn remove ... command, and not with yarn add ....

Changes the husky.local.sh from this (which works fine):

packageManager=yarn
cd "."

to this (the hook stops running on commit):

packageManager=yarn
cd "packages/client/"

@bertdeblock
Copy link

Just tested this again as well and I can confirm it only seems to happen when running yarn remove ....

@eduard-malakhov
Copy link

There is a special node on using husky with monorepos: https://github.com/typicode/husky#monorepos In short for monorepos, it is recommended to only install husky into the root package. Otherwise, all sorts of conflicts are expected when husky installation script is run for several packages in the repo.

I use husky with lerna and yarn workspaces by installing husky to the root package via yarn add --dev -W husky. The -W/--ignore-workspace-root-check flag allows installation into the root package (https://classic.yarnpkg.com/en/docs/cli/add#toc-yarn-add-ignore-workspace-root-check-w). Personally, I haven't seen any downsides of this approach so far.

@tad3j
Copy link
Author

tad3j commented Mar 23, 2020

@eduard-malakhov , I have it installed with -W flag in root, but still, it looks like if you accidental run yarn remove (according to @melanieseltzer, haven't confirmed) it will mess up the .git\hooks, without you even knowing that husky has stopped working.

@bertdeblock
Copy link

I only installed Husky in the root as well.

@eduard-malakhov
Copy link

@tad3j , I've tried the following:

  • running yarn remove -W husky in the monorepo root — the package as well as its hooks have been removed;
  • running yarn remove -W husky in a package directory — yarn reports that it can't remove the package as it is not listed in the package.json.

May it be that when you were trying to remove husky, it was listed in any of the packages besides the root? If it is indeed and issue with removal and you can reproduce this situation, running yarn with the --verbose flag might give more information.

@bertdeblock
Copy link

The issue is not about removing Husky itself, but about removing another dependency from inside a package directory.

@tad3j
Copy link
Author

tad3j commented Mar 23, 2020

@eduard-malakhov as @bertdeblock says...I also never tried to remove husky, this seems to happen when running yarn (maybe also lerna) in a sub directory.

@eduard-malakhov
Copy link

eduard-malakhov commented Mar 23, 2020

@tad3j , @bertdeblock thank you guys, now I see, I have reproduced the issue.

Steps to reproduce

  1. Create a monorepo using lerna with yarn workspaces enabled.
  2. Add husky as dependency in the root package.
  3. Configure a pre-commit hook in the root package.
  4. Change directory to one of the packages in the monorepo.
  5. Remove any valid dependency of that package by running yarn remove <any dependency>.
  6. Stage and commit any changes.

Expected result
The specified pre-commit hook is executed by husky prior to commit.
Actual result
The hook is not executed.

Presumably, the same behaviour can be observed with other commands that trigger package reinstallation, like yarn install <...>.

I will take a closer look at this issue later today.

Update:
For the record, .git/hooks/husky.local.sh is updated as follows.
Before:

# Created by Husky v4.2.3 (https://github.com/typicode/husky#readme)
#   At: 3/23/2020, 6:59:15 AM
#   From: /workspaces/instrumentation/node_modules/husky (https://github.com/typicode/husky#readme)

packageManager=yarn
cd "."

After:

# Created by Husky v4.2.3 (https://github.com/typicode/husky#readme)
#   At: 3/23/2020, 10:38:18 AM
#   From: /workspaces/instrumentation/node_modules/husky (https://github.com/typicode/husky#readme)

packageManager=yarn
cd "packages/test_package/"

@naorzr
Copy link

naorzr commented Apr 23, 2020

@eduard-malakhov any updates on this?

@fixpunkt
Copy link

fixpunkt commented Apr 24, 2020

I can even reproduce it like this:

  1. Checkout a fresh working copy of a yarn workspaces monorepo as described by @eduard-malakhov in Husky setting wrong folder in monorepo (lerna/yarn workspace) #677 (comment).
  2. cd into one of the workspaces.
  3. Run yarn install.

Being able to just run yarn install from any place in the monorepo and having it install dependencies for the entire monorepo is a supported feature of yarn workspaces. Note that the problem can only be observed on the initial yarn install.

@eduard-malakhov
Copy link

@naorzr, although we have a pretty good understanding of why this is happening, fixing this behavior is not trivial. As far as I can tell, this behavior is due to the way the hooks were designed in husky in the first place. Fixing the issue requires a substantial change in the way hooks are managed by husky and the risks are high that fixing this issue might break other use cases. Making the situation worse, if hooks stop working, it might not be immediately apparent to the users because hooks tend to fail without notice. Given the wide adoption of husky and the limited attention that this issue has received, I believe that fixing this issue at the risk of breaking mainstream scenarios is not worthwhile.

To support this fix and make sure it doesn't break anything we'll need to extend our tests to cover many more use cases than we've got today for all supported package managers, which is quite a bit of work. I've started adding tests but I am not done yet. I have even considered releasing a separate package with the redesigned approach to hooks management, but it seems to be overkill to fork and maintain a new repository to only fix one issue.

I am using husky in most of my monorepos myself and though, as @fixpunkt has correctly pointed out, "being able to just run yarn install from any place in the monorepo" is a valid use case, I think the best workaround, for now, is to avoid running yarn install and similar commands from anywhere except the repository root.

@jparkrr
Copy link

jparkrr commented Apr 28, 2020

Thanks so much for looking into this.

From my limited perspective, everything that you said about the difficulty of this issue makes sense except for the fact that this works fine on version 3.1.0 (as far as I can tell). What do we make of that?

@eduard-malakhov
Copy link

@jparkrr, you're right about version 3.1.0. There was a major refactoring in version 4.0 that moved most of the common logic from respective .git/hooks/* scripts into external husky.sh and husky.local.sh (#654). This looks like a reasonable optimization to me and I can't see exactly why this new approach fails when yarn is run from a folder other than the repository root. Maybe @typicode could help us out.

@GuillaumeAmat
Copy link

Thanks for the feedbacks @eduard-malakhov 👌

I would like to clarify a point though. You said that the bug occurs "when yarn is run from a folder other than the repository root", but I use it from the root and it fails too.

Eg:

$ yarn workspace MY_PACKAGE remove MODULE

Under the hood, Yarn runs the remove command in the subpackage but the command is launched at the root level.

@eduard-malakhov
Copy link

Good point, @GuillaumeAmat, thank you! I have missed this case out.

@dezkareid
Copy link

Someone has a workaround for this issue? I was thinking in create a npm scrip to exec yarn remove and reinstall husky, but I don't know if that is the better approach

@chmyaf
Copy link

chmyaf commented May 24, 2020

@dezkareid, my workaround on Linux:
chmod -w ./.git/hooks/husky.local.sh

@dcastil
Copy link

dcastil commented May 24, 2020

I didn’t test it sufficiently, but it seems like husky saves the location from which the initial yarn install was run after husky was added by someone else in .git/hooks/husky.local.sh. E.g. if the initial yarn install was run in packages/web-app, it contains a line like this:

cd "packages/web-app"

which needs to be changed to

cd "."

in order for husky to run properly.

My workaround for this is to add a postinstall script into the package.json of all workspaces:

{
  "scripts": {
    "postinstall": "sed -i '' -E 's/cd \".+\"/cd \".\"/' ../../.git/hooks/husky.local.sh"
  }
}

@bnbarak
Copy link

bnbarak commented May 26, 2020

How about a flag to prevents husky from overriding the cd "." in git/hooks/husky.local.sh?
something like:

if(HUSKY_RUN_FROM_PROJECT_ROOT) {
  cd = "."
} else {
  cd = ... // do whatever you do now
}

This was referenced May 26, 2020
@JonKrone
Copy link

+1 for this issue's priority, a team I'm on is running into this issue often as well. The workaround of only running yarn commands in a monorepo's root is hard to engrain in devs' workflow and things that get past some githooks take some extra coordination to fix again. For the time being, we'll be using the patch in #728

@TheComputerM
Copy link

+1, git hooks have been a terrible problem in yarn workspaces.

@bnbarak
Copy link

bnbarak commented Nov 24, 2020

I commented back on the patch PR #728 - I think it is ready to merge.

@mfakhrusy
Copy link

recently faced with this as well, I think @bnbarak PR should fix the problem and should have the priority for the next patch

@bnbarak
Copy link

bnbarak commented Dec 3, 2020

@JonKrone can you please merge #728. I added testing instructions.

@JCMais
Copy link

JCMais commented Jan 6, 2021

Just got caught on this issue. The postinstall script @dcastil posted above was the best workaround I found.

@nickgieschen
Copy link

@dcastil's solution fails if the build from inside the package. E.g. Netlify does this for monorepos. A slight adjustment to get it to work is:

"postinstall": "if [[ -f ../../.git/hooks/husky.local.sh ]]; then sed -i '' -E 's/cd \".+\"/cd \".\"/' ../../.git/hooks/husky.local.sh; fi" }

ghost pushed a commit to flaves/peintagone that referenced this issue Jan 24, 2021
post install script was set to all workspaces to reset husky.local.sh -> see typicode/husky#677
@jamiehaywood
Copy link
Contributor

jamiehaywood commented Feb 28, 2021

I thought I would add my two cents as I had a junior dev bypass the postinstall script suggestion @nickgieschen suggested using --ignore-scripts

I created a huskypostinstall.ts at the top level of the monorepo containing:

import fs from 'fs';

const huskyHookLoc = '../../.git/hooks/husky.local.sh';

if (fs.existsSync(huskyHookLoc)) {
  fs.readFile(huskyHookLoc, 'utf-8', (err, data) => {
    if (err) throw err;
    const newData = data.replace(/cd.*$/gim, 'cd "."');

    fs.writeFile(huskyHookLoc, newData, 'utf-8', (err) => {
      if (err) throw err;
      // eslint-disable-next-line no-console
      console.log('husky post-install fix applied');
    });
  });
}

process.exit(0);

and then added it as the "postinstall" script in each repo:
"postinstall": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} ts-node ../../huskypostinstall.ts"

Your mileage may vary with the above (we're using TS & modules etc.) but the general principles are:

  1. use fs to read from the hooks script file
  2. use /cd.*$/gim to replace the cd line with cd "."
  3. write back to file

igarashitm added a commit to igarashitm/atlasmap that referenced this issue Jun 28, 2021
Fixes: atlasmap#3000
Fixes: atlasmap#3011
Disabled react-scripts preflight check
Also downgraded husky to 4.2.3 as v6 has an issue with monorepo - typicode/husky#677
igarashitm added a commit to atlasmap/atlasmap that referenced this issue Jun 30, 2021
Fixes: #3000
Fixes: #3011
Disabled react-scripts preflight check
Also downgraded husky to 4.2.3 as v6 has an issue with monorepo - typicode/husky#677
@stale
Copy link

stale bot commented Jul 2, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jul 2, 2021
@Ashoat
Copy link

Ashoat commented Jul 2, 2021

Hey, just wanted to ping this thread to make sure it doesn't get closed as stale. We're experiencing this issue with Yarn Workspaces and would love to see it fixed.

@stale stale bot removed the wontfix label Jul 2, 2021
@jamiehaywood
Copy link
Contributor

jamiehaywood commented Jul 3, 2021

@Ashoat we found that if you upgrade to husky v6 it fixed this

Ashoat added a commit to CommE2E/comm that referenced this issue Jul 5, 2021
Summary:
It [should fix](typicode/husky#677 (comment)) the issue where `yarn add` or `yarn remove` break `lint-staged`, which was occurring because we have a monorepo.

Note that there were a good amount of breaking changes in this migration so the config looks pretty different.

Test Plan: `yarn cleaninstall`, then make a commit and make sure `lint-staged` still runs

Reviewers: palys-swm, atul

Reviewed By: palys-swm, atul

Subscribers: KatPo, Adrian

Differential Revision: https://phabricator.ashoat.com/D1600
@stale
Copy link

stale bot commented Sep 1, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests