use-yarn-instead icon indicating copy to clipboard operation
use-yarn-instead copied to clipboard

Is there a good way to force developers to use yarn instead of npm?

Open alexanderwallin opened this issue 8 years ago • 24 comments

This experimental module came from the discussion in https://github.com/yarnpkg/yarn/issues/1732, where the question was how to prohibit the use of npm on a project and point developers to using yarn instead.

This module doesn't really solve that problem, but merely logs things that are easy to miss. @farisj tried a couple of other things, but none did work.

So the question is, is there a way or hack to for instance hijack npm install? Should this even be necessary?

alexanderwallin avatar Dec 16 '16 14:12 alexanderwallin

Isn’t this a little bit rude?

Seriously, suggesting yarn is one thing, forcing it is another. There’s a contract that both are respecting, and that npm carefully crafted in years. Also you are incorrectly inferring that there’s just npm and yarn, but actually there are tons of other package managers for completely unexpected environments — from atom to Java platforms.

yuchi avatar Feb 04 '17 12:02 yuchi

So my question is, why would you ever want to force it?

yuchi avatar Feb 04 '17 12:02 yuchi

My own use case for this is such: I am working on a medium size team and we are considering moving one of our applications to yarn over npm. However, I'd want to ensure everyone on my team is using yarn commands over npm. Yes, I can tell everyone to use yarn run over npm run, but I want to correct for human error and force that behavior.

With Rails and Bundler, for instance, bundler will prevent me from booting my rails server if I don't prepend my commands with bundle exec in scenarios where I'm working with the wrong versions of gems. This behavior ensures we're using all of the gems specified in the Gemfile.lock, even if people have global gems installed elsewhere, etc.

It would greatly ease the transition of my coworkers remembering to use yarn on our app if they had no choice about the matter. For me, this feature is about managing a team and ensuring that everyone has the same setup. Just having the yarn.lock file is pointless if I can't get my fellow employees to use it.

@yuchi , your comment about this issue being rude is unnecessary. I agree that I wouldn't want to force other developers to use it outside of my own company, but internally we are trying to optimize our workflow across the team, so having a way to force the use of yarn is very desirable to me.

farisj avatar Feb 05 '17 02:02 farisj

@farisj it‘s totally understandable then, but I’m scared by the possibility of this practice becoming widespread. We’ve seen this on the iojs/node debate—and while this one is not an issue on political ground, I’m a little disenchanted on people by now.

To support my question, I got to this repo by because someone I follow stargazed it. But the rationale you explained was nowhere to be found. A little warning «don’t use it on modules—for enterprise use only!» would help a lot in communicating the goal of this project.

yuchi avatar Feb 05 '17 11:02 yuchi

I agree the README does not explain the purpose of this – almost entirely experimental – repo very well. I'll add a clearer description!

I disagree that the enforcing of a certain tool is any more rude than choosing a database or linting code. Like @farisj said, it's about assuring quality and shipping software that works.

alexanderwallin avatar Feb 06 '17 11:02 alexanderwallin

https://github.com/alexanderwallin/use-yarn-instead/commit/b4740bcd60f13fed3c2d468ecc565b1991a96871

alexanderwallin avatar Feb 06 '17 12:02 alexanderwallin

Thank you!! Looks great :)

By the way probably I will be using it in a project or two, so thanks twice!

yuchi avatar Feb 06 '17 12:02 yuchi

Ah, cool! Please come back with any feedback you might have.

alexanderwallin avatar Feb 06 '17 12:02 alexanderwallin

I have the exact same situation as @farisj explained above. What's the point of using yarn if I can't make sure that yarn.lock doesn't get out of sync? Want to avoid other to run npm i or modifying directly package.json without using yarn

rafayepes avatar Feb 20 '17 12:02 rafayepes

npm and yarn seem to both extend the process.env when executing scripts ... https://github.com/npm/npm/blob/5d17fc945bcf48b69bc0dc4741028762f6bca02c/lib/utils/lifecycle.js#L76 and https://github.com/yarnpkg/yarn/blob/508c959080c52183697602f52ebb95b086b6b3d3/src/util/execute-lifecycle-script.js#L33 ...

So I just went full dictator and this is what I got: "preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\"",

It does the job.

Sinewyk avatar Apr 07 '17 15:04 Sinewyk

Holy moly! That's some impressive digging. Feel free to fire away a PR if you want.

alexanderwallin avatar Apr 10 '17 09:04 alexanderwallin

Just released a module that includes a CLI to do this (useful for preinstall scripts): https://github.com/AndersDJohnson/use-yarn

Stay tuned, as I may have more ideas coming, especially around CI.

FYI @alexanderwallin @Sinewyk @farisj @rafayepes

AndersDJohnson avatar May 06 '17 04:05 AndersDJohnson

I've also just released a helper for Danger to check for missing yarn.lock changes on CI: https://github.com/AndersDJohnson/danger-yarn-lock

FYI @alexanderwallin @Sinewyk @farisj @rafayepes

AndersDJohnson avatar May 07 '17 22:05 AndersDJohnson

I solve that with husky and lint-staged and adding the following to package.json:

{
  "scripts": {
     "precommit": "lint-staged"
  },
  "lint-staged": {
    "+(package.json|yarn.lock)": [
      "node ./scripts/yarn-check.js"
    ]
  },
  "devDependencies": {
    "husky": "^0.13.2",
    "lint-staged": "^3.4.0"
  }
}

And then, that yarn-check.js script is

'use strict';

const execSync = require('child_process').execSync;
const chalk = require('chalk');

try {
    execSync('yarn check').toString();
} catch (error) {
    console.log(
        '\n' +
            chalk.red(`  This project uses ${chalk.underline.bold('yarn')} to install all JavaScript dependencies.\n`) +
            chalk.red(`  Please don't modify the dependencies manually in package.json nor use npm install to update them.\n`) +
            chalk.dim('    Please run ') +
            chalk.reset('yarn add [package-name]') +
            chalk.dim(' to install any new dependency\n') +
            chalk.dim('    or') +
            chalk.reset(' yarn upgrade [package@version]') +
            chalk.dim(' to upgrade a dependency to a specific version.\n') +
            chalk.dim(
                '    Ensure you commit both the package.json and the yarn.lock files together.\n'
            ) +
            chalk.dim('    Check out ') +
            chalk.underline.blue('https://yarnpkg.com/en/docs/cli/') +
            chalk.dim(' for more information.') +
            `\n`
    );
    process.exit(1);
    throw error;
}

FYI @alexanderwallin @Sinewyk @farisj @adjohnson916

rafayepes avatar May 08 '17 18:05 rafayepes

@rafayepes Nice! That works on local machines, but yarn check could be run on CI too.

AndersDJohnson avatar May 09 '17 03:05 AndersDJohnson

@AndersDJohnson on CI you can install dependencies using yarn install --frozen-lockfile --non-interactive, which will fail if yarn.lock was not properly updated.

rafayepes avatar Nov 09 '17 11:11 rafayepes

@alexanderwallin I've found that running yarn check on precommit and yarn install --frozen-lockfile --non-interactive to install dependencies on CI ensures that yarn is used instead of npm. Does that answer your initial question?

As extra info, we are also running now yarn install --pure-lockfile --non-interactive on both postcheckout and postmerge, to ensure everyone has always latest dependencies installed

rafayepes avatar Nov 09 '17 11:11 rafayepes

npm install packageName how hook

amorist avatar Jul 17 '18 07:07 amorist

@rafayepes It seems you've got some nice solutions going here. I think I'll leave this issue open indefinitely to allow different solutions to be published, but big thanks for sharing!

(Not a yarn user myself, so won't evaluate specific solutions.)

🎈

alexanderwallin avatar Aug 02 '18 14:08 alexanderwallin

The above helps for plain npm installs, but doesn't work when installing/adding a package to an app. To solve for this, I intentionally create a broken package-lock.json file with the following inside it

See_package-lock.json

Please don't use npm within this repo; please use yarn instead.

This makes it so when I npm i or npm i --save-dev typescript, this occurs:

image

This does create a warning when yarn installing/adding, but I think that's fine as this definitively prevents using npm.

skoshy avatar Oct 19 '19 17:10 skoshy

  1. add script

package.json

    "preinstall": "node ./scripts/checkYarn.js",

npm will auto-execute the script.

  1. checkYarn.js
if (!/yarn\.js$/.test(process.env.npm_execpath || '')) {
  console.warn(
    '\u001b[33mThis repository requires Yarn 1.x for scripts to work properly.\u001b[39m\n'
  )
  process.exit(1)
}

when you execute npm install, it will error

image

alanhe421 avatar Jul 26 '20 02:07 alanhe421

Set engines in package.json

{
...
  "engines": {
    "npm": "use yarn instead of npm."
  }
}

When you use npm i

npm ERR! code ENOTSUP
npm ERR! notsup Unsupported engine for {project-name}@{version}: wanted: {"npm":"use yarn instead of npm."} (current: {"node":"14.5.0","npm":"6.14.5"})
npm ERR! notsup Not compatible with your version of node/npm: {project-name}@{version}
npm ERR! notsup Not compatible with your version of node/npm: {project-name}@{version}
npm ERR! notsup Required: {"npm":"use yarn instead of npm."}
npm ERR! notsup Actual:   {"npm":"6.14.5","node":"14.5.0"}

tktcorporation avatar Jul 29 '20 17:07 tktcorporation

@tktcorporation Do you put that in the project's package.json? Or in this package which you install as a dependency?

alexanderwallin avatar Sep 25 '20 16:09 alexanderwallin

@alexanderwallin

@tktcorporation Do you put that in the project's package.json? Or in this package which you install as a dependency?

I put it like this.

{
  "name": "sample",
  "version": "1",
  "description": "",
  "author": "tktcorporation",
  "engines": {
    "npm": "use yarn instead of npm."
  },
  "scripts": {
    "build": "tsc",
  },
  "dependencies": {},
  "devDependencies": {}
}

and write engine-strict=true on a file .npmrc

ref: https://qiita.com/suin/items/a7bf214f48eb9b2d9afc

Another

I found another way to do that.

{
  "name": "sample",
  "version": "1",
  "description": "",
  "author": "tktcorporation",
  "scripts": {
    "preinstall": "npx only-allow yarn"
  },
  "dependencies": {},
  "devDependencies": {}
}

It is needed to put "preinstall": "npx only-allow yarn" to scripts. And it is not needed to install only-allow

ref: https://qiita.com/suin/items/e0fbdd9af1150138a65c

tktcorporation avatar Sep 25 '20 18:09 tktcorporation