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 install fails when using --prod #914

Closed
ext opened this issue Mar 28, 2021 · 38 comments
Closed

husky install fails when using --prod #914

ext opened this issue Mar 28, 2021 · 38 comments
Labels

Comments

@ext
Copy link

ext commented Mar 28, 2021

I have a package.json similar to:

{
  "scripts": {
    "prepare": "husky install"
  },
  "devDependencies": {
    "husky": "^5.2.0"
  }
}

Running npm install --prod (or NODE_ENV=production) will only install production dependencies and thus node_modules/.bin/husky will not be present. In my case this happens when trying to build a docker container where I want to install only the production dependencies.

$ npm install --prod

> foobar@1.0.0 prepare
> husky install

sh: line 1: husky: command not found
npm ERR! code 127
npm ERR! path /home/ext/foobar
npm ERR! command failed
npm ERR! command sh -c husky install

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/ext/.npm/_logs/2021-03-28T15_18_18_181Z-debug.log

What is the suggested way to handle this scenario?

With husky 4 it worked properly as husky install wasn't needed.

@pinalbhatt
Copy link

adding --ignore-scripts worked for me
RUN npm ci --only=production --ignore-scripts

@ext
Copy link
Author

ext commented Mar 30, 2021

Thank you for the tips, maybe it could be added to the docs in case others face the same issue?

@typicode
Copy link
Owner

typicode commented Mar 30, 2021

Yes, good suggestion. PR welcome.

@rdmurphy
Copy link

rdmurphy commented Apr 9, 2021

I had a similar issue and the problem with using a blanket --ignore-scripts is it affects all packages being installed. So for example, I had a project that needed scripts to run for installing a binary (like imagemin-jpegtran) because a package using it appeared in dependencies.

I don't know what the solution is, but it'll be difficult to consider husky as an option if using --production will only work if no other dependencies do need scripts to run on install, which is often out of my hands.

@robhowell
Copy link

I just hit this problem found this issue, and discovered that there is an official recommended approach, using the is-ci module: https://typicode.github.io/husky/#/?id=disable-husky-in-cidocker

@ext
Copy link
Author

ext commented Apr 26, 2021

I just hit this problem found this issue, and discovered that there is an official recommended approach, using the is-ci module: https://typicode.github.io/husky/#/?id=disable-husky-in-cidocker

Most of the linked approaches wont help, --ignore-scripts is the best (only?) option so far.

  • Setting HUSKY=0 wont help as prepare/postinstall will still try to execute husky install (causing command not found).
  • Using [ -n "$CI" ] (or similar) in the scripts assume husky is a production dependency, if it is a devDependency it still causes command isn't found.
  • Using is-ci would only work if is-ci is a production dependency or it will also fail because is-ci isn't found instead.

Additionally building docker images locally will not trigger CI or is-ci as it is not running in a CI environment.

I guess another hack that might work would be:

# install all dependencies, get past prepare/postinstall
npm install --production=false

# remove all non-production dependencies
npm prune --production

But that would be really wasteful and inefficient.

@pinalbhatt
Copy link

For now i am using this solution RUN npm set-script prepare "" && npm ci --only=production in my Docker file

@vith
Copy link

vith commented May 5, 2021

  • Using is-ci would only work if is-ci is a production dependency or it will also fail because is-ci isn't found instead.

is-ci contributes 84 KiB to node_modules, so this is not too bad.

"scripts": {
    "prepare": "is-ci || husky install"
}

works for me with one caveat: is-ci doesn't seem to detect npm ci --production as being "CI"...

At least CI=1 npm ci --production can complete successfully now.

@ext
Copy link
Author

ext commented May 5, 2021

[..] so this is not too bad

I have to disagree with you there.

  • It erodes the purpose of production dependencies. If the production dependencies is a mix of runtime and buildtime dependencies why even keep them separate?
    • This becomes worse if you use any kind of license or security scanner as it produces more noise when scanning dependencies which won't really affect the runtime.
  • It sets a bad precedent for other packages, if this would be the solution it could quickly lead to many other packages needing the same treatment.
  • 84 KiB is still 84 KiB too much in my opinion, similarly there is one transitive dependency ci-info being pulled as well.
  • If it would be in a library all the consumers of the package would get this dependency as well (and possibly conflicting versions).

I still value your suggestion thought. Maybe it will help someone else?

@ismay
Copy link

ismay commented May 22, 2021

I think this is related: #981. Core topic seems that the lifecycle hooks are sometimes triggering in circumstances where we don't want them to run (ci, installation as a dependency (production install)). I can see the appeal of having husky be agnostic to what enviroment it's being installed in, but the recommendations currently in the docs don't seem to cover all potential scenarios.

@stale
Copy link

stale bot commented Jul 21, 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 21, 2021
@rchl
Copy link

rchl commented Jul 21, 2021

Valid issue IMO and the workarounds above are just workarounds.

@stale
Copy link

stale bot commented Sep 19, 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 Sep 19, 2021
@ext
Copy link
Author

ext commented Sep 19, 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.

I still consider this to be an issue.

@rchl
Copy link

rchl commented Nov 14, 2021

The way the package works after v4 is problematic as evident by this and other related issues reported in this repo. I wonder why author doesn't want to acknowledge that the new way of handling hooks is just too problematic (even if intentions were good) and probably should be reverted.

@cujarrett
Copy link

cujarrett commented Dec 1, 2021

I'm running into this out of the blue on one two of my apps w/ husky 7.0.4.

I've deleted node_modules, package-lock.json and ran npm install to rebuild them w/ no success.

I've edited my deploy job to npm ci --only=production --ignore-scripts w/ no success.

Big sad.

@cujarrett
Copy link

I solved my occurance of this with @pinalbhatt 's suggestion of npm set-script prepare "" && npm ci --only=production.

@justinhaaheim
Copy link

The "custom script" option listed in the husky docs worked best for me while deploying a small hobby project to Heroku, which is not supported by the is-ci tool. The fact that it's a node script adds the flexibility to check for production environment:

// prepare.js

const isProduction = process.env.NODE_ENV === 'production';
const isCi = process.env.CI !== undefined;

if (!isCi && !isProduction) {
  require('husky').install();
}

@stale
Copy link

stale bot commented Feb 20, 2022

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 Feb 20, 2022
bamse16 added a commit to bamse16/fetch-retry that referenced this issue Feb 21, 2022
If `husky` is not present, `npm` fails to install `fetch-retry` as a dependency for `production` or `ci` environments.

```
npm ERR! code 127
npm ERR! git dep preparation failed
npm ERR! command /home/ubuntu/.nvm/versions/node/v12.22.10/bin/node /home/ubuntu/.nvm/versions/node/v12.22.10/lib/node_modules/npm/bin/npm-cli.js install --force --cache=/home/ubuntu/.npm --prefer-offline=false --prefer-online=false --offline=false --no-progress --no-save --no-audit
npm ERR! > fetch-retry@5.0.1 prepare
npm ERR! > husky install
npm ERR! npm WARN using --force Recommended protections disabled.
npm ERR! sh: 1: husky: not found
npm ERR! npm ERR! code 127
npm ERR! npm ERR! path /home/ubuntu/.npm/_cacache/tmp/git-clone-9f0cb372
npm ERR! npm ERR! command failed
npm ERR! npm ERR! command sh -c husky install
npm ERR!
npm ERR! npm ERR! A complete log of this run can be found in:
npm ERR! npm ERR!     /home/ubuntu/.npm/_logs/2022-02-21T00_27_56_429Z-debug.log

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/ubuntu/.npm/_logs/2022-02-21T00_27_56_749Z-debug.log
```

They offer a few alternative to the "default install" here: https://typicode.github.io/husky/#/?id=disable-husky-in-cidockerprod. See also typicode/husky#914.
@elyobo
Copy link

elyobo commented Mar 3, 2022

We're seeing this with Google App Engine (node flex environment), where it's Google doing the npm install that's somehow kicking this off. Can't reproduce it locally 🤷

@stale
Copy link

stale bot commented May 3, 2022

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 May 3, 2022
@typicode typicode removed the wontfix label May 3, 2022
@malko
Copy link

malko commented May 19, 2022

My 2 cents to this issue that make our CI pipelines fail too.
Using the prepare npm script is IMHO not a good idea at all. the prepare script is called not only on install but also on publish and perhaps i'm missing something but I don't want it to be executed on publish at all.
Also it will make stuffs more complicated if you intend to use that prepare script for yourself as you will have to deal with including "husky install" somewhere in your prepare script.

Finally I think best options so far are:

  • npm set-scripts prepare "" before running install or publish during you CI jobs.
  • replace your prepare script with:
    "prepare": "if [[ -x \"$(command -v husky)\" ]]; then husky install; fi"

I can't recommend the --ignore-scripts as it will avoid all other legitimate script to be run, it can't be a good solution.

@joaosouzaminu
Copy link

My 2 cents to this issue that make our CI pipelines fail too. Using the prepare npm script is IMHO not a good idea at all. the prepare script is called not only on install but also on publish and perhaps i'm missing something but I don't want it to be executed on publish at all. Also it will make stuffs more complicated if you intend to use that prepare script for yourself as you will have to deal with including "husky install" somewhere in your prepare script.

Finally I think best options so far are:

  • npm set-scripts prepare "" before running install or publish during you CI jobs.
  • replace your prepare script with:
    "prepare": "if [[ -x \"$(command -v husky)\" ]]; then husky install; fi"

I can't recommend the --ignore-scripts as it will avoid all other legitimate script to be run, it can't be a good solution.

Just a nit pick, the npm command actually is set-script in singular.

@ThePlenkov
Copy link

ThePlenkov commented Jul 18, 2022

I have solved the same problem by usage of an own tiny CLI:
"prepare": "check NODE_ENV=production || husky install"
where check is installed by:
npm install check-env-cli

The only point - this module has to be then production dependency then . Meanwhile why I see is a good way

  • it's crosss env, while using is OS-dependent
  • it works with predefined pipelines ( sometimes command like npm ci --production is defined outside of the project in an external builder ) So you cannot inject there parameters like --omit..
  • this checker allows to use any environment variable - so you can make condition even more specific. --production flag triggers NODE_ENV=production - that's why I use it and is enough.

Alternative usage. If you wish not to have this module as a global dependency - you can also use npx:

    "prepare": "npm run is-prod || husky install",
    "is-prod": "npx -y check-env-cli NODE_ENV=production"

@stale
Copy link

stale bot commented Sep 16, 2022

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 Sep 16, 2022
@ext
Copy link
Author

ext commented Sep 18, 2022

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.

I still consider this to be an issue.

@stale stale bot closed this as completed Sep 26, 2022
@glensc
Copy link
Contributor

glensc commented Sep 26, 2022

I have tried alternative approaches instead of using prepare script:

  1. add is-ci to dev dependencies
  2. "postinstall": "is-ci || husky install || echo Skipped husky setup"

this will just show error when binary is not available. but using this script is a problem while prepare is ran at local dependencies install then "postinstall" is also executed when package is used as dependency. echo was chosen for windows compatibility, as constructs like || : or || true don't work there.

also, while the most problematic place is CI invocation, the check should be really is-dev to account for scenarios where you run yarn install --production locally

@stec00
Copy link

stec00 commented Sep 29, 2022

Updated version of the popular workaround for this given above from @pinalbhatt - since later npm versions give warnings that set-script and --prod are both deprecated:

npm pkg delete scripts.prepare && npm install --omit=dev

(Note the single space rather than empty string - have raised a SO question in case some further light can be thrown on this: https://stackoverflow.com/questions/73896448. UPDATE - got a good answer to this and have now updated this one accordingly.)

@glensc
Copy link
Contributor

glensc commented Sep 29, 2022

I wanted to say just use npm pkg delete scripts.prepare, but you already got it :)

@ianchanning
Copy link

The "custom script" option listed in the husky docs worked best for me while deploying a small hobby project to Heroku, which is not supported by the is-ci tool. The fact that it's a node script adds the flexibility to check for production environment:

// prepare.js

const isProduction = process.env.NODE_ENV === 'production';
const isCi = process.env.CI !== undefined;

if (!isCi && !isProduction) {
  require('husky').install();
}

Yes - I like this the best as then I can document in that script why I have to do all these workarounds. With all other solutions they will just be undocumented workarounds buried in CI tools or package.json.

@GabrielDelepine
Copy link

GabrielDelepine commented Jan 10, 2023

There are a large variety of approaches, and not one is satisfying

I personally prefer

{
  "prepare": "test -d node_modules/husky && husky install || echo \"husky is not installed\"",
}

IMO, it's evidence that many projects are getting deployed with dev dependencies, which is sad to realize

@NachtRitter
Copy link

If you use command like npm ci --omit=dev you can access those property via $npm_config_omit.
According to value of $npm_config_omit you can decide to run husky install command or not.

{
  "postinstall": "if [ \"$npm_config_omit\" = \"dev\" ]; then echo \"Skip husky installation\"; else husky install; fi"
}

@mickael-h
Copy link

If you use command like npm ci --omit=dev you can access those property via $npm_config_omit. According to value of $npm_config_omit you can decide to run husky install command or not.

{
  "postinstall": "if [ \"$npm_config_omit\" = \"dev\" ]; then echo \"Skip husky installation\"; else husky install; fi"
}

Best solution by far. Doesn't require extra packages, or modifying the run script.

@kostia1st
Copy link

kostia1st commented Oct 1, 2023

"prepare": "if [[ -x \"$(command -v husky)\" ]]; then husky install; fi"

@malko Thanks for the tip, this actually did the job for me. 🙏

UPD: Had to adjust it a bit to make it also work in my local env:

"prepare": "sh -c 'if [[ -x \"$(command -v husky)\" ]]; then  husky install; fi'",

@kael-shipman
Copy link

My take:

{
  "prepare": "command -v husky &>/dev/null && husky install || true"
}

Don't know how cross-env it is, but works just fine for me on linux.

@lucasmelosilva
Copy link

adding --ignore-scripts worked for me RUN npm ci --only=production --ignore-scripts

In version 8.x and above use --omit=dev flag to install only regular dependencies:
RUN npm ci --omit=dev --ignore-scripts
This will install only dependencies, and not devDependencies, regardless of the value of the NODE_ENV environment variable.
If you use 6.x or an earlier version, you need to use the --only=prod flag instead.

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