Can I run "npm ci" with a custom npmrc file? - node.js

I want to have different paths for different situations for npmrc. For example I want to have a "npmrc.dev" for devs and one for CI "npmrc.ci".
Is this possible? I would be happy with any hack, param, environment variable or some npm module.

Related

How does `CI=true` affect the `npm install` command?

I'm having trouble finding good documentation on how the CI=true effects the npm install command.
How is CI=true npm install different from npm install.
Does the CI=true even effect the install?
I tried it locally and it does NOT cause it to behave like npm ci. I tried running it without the package-lock.json or npm-shrinkwrap.json file present. It created the lock file.
Also, is there a difference between
CI=true npm install
and
CI=true
npm install
and
export CI=true
npm install
Setting the CI environment variable to true effects how npm gathers usage data.
How it effects npm is explained in the Does npm send any information about me back to the registry section of the docs (For convenience, I've provided a verbatim copy of that section below). Consider particularly the description about the Npm-In-CI header.
In summary, setting CI=true causes npm to set the Npm-In-CI header to true, thus as a result the data gathered (by npm) assumes the package(s) are being installed via a "build farm", (i.e. for Continuous Integration purposes), instead of a "human".
The following is what is stated in the docs at the aforementioned link:
Does npm send any information about me back to the registry?
Yes.
When making requests of the registry npm adds two headers with information about your environment:
Npm-Scope – If your project is scoped, this header will contain its scope. In the future npm hopes to build registry features that use this information to allow you to customize your experience for your organization.
Npm-In-CI – Set to “true” if npm believes this install is running in a continuous integration environment, “false” otherwise. This is detected by looking for the following environment variables: CI, TDDIUM, JENKINS_URL, bamboo.buildKey. If you’d like to learn more you may find the original PR interesting. This is used to gather better metrics on how npm is used by humans, versus build farms.
With regards to the specific part of your question, i.e.
Does the CI=true even effect the install?
Generally "No", there is no notable difference to the resultant installation by npm with or without CI=true.
However, a possible scenario whereby the installation of a package by npm could/may be effected, is if a package author defined a postinstall script in package.json that performed different conditional logic if/when the CI environment variable is set to true.
Regarding the last part of your question, i.e.
Also, is there a difference between ...
The accepted answer to this question addresses that.
In summary;
Your first two commands are similar - they both set the environment variable for the current shell only.
However your last example that utilizes export sets the environment variable for the current shell and all processes started from the current shell.

Replace env variables in the .npmrc with dotenv before `npm install`

I have to work with some packages in the private registry. So, in my package.json in the dependencies section I have a lines like this one:
...
"dependencies": {
"#myco/my-awesome-package": "^0.4.5",
...
}
...
There is authentication required for the private registry, so I have to create the .npmrc file in my project:
registry=https://registry.npmjs.org/
#myco:registry=https://myco-registry-path/
//myco-registry-path/:username=${MYCO_REGISTRY_USER}
//myco-registry-path/:_password=${MYCO_REGISTRY_PASSWORD_BASE64}
Yes, I know about _authToken, but in my case it is easier to use user and password.
Anyway, here you can see two env variables: ${MYCO_REGISTRY_USER} and ${MYCO_REGISTRY_PASSWORD_BASE64} which I have to replace before npm install.
I know the very simple solution for this problem: put them to the "global" env variables for example to my .bash_profile (or any terminal profile of your choice).
But I do not want to keep variables like this in the "global" scope because the are important only for the current project. What I want to do is to use dotenv. I want to create a .env file in the root of my project:
MYCO_REGISTRY_USER=myco-registry-username-value
MYCO_REGISTRY_PASSWORD_BASE64=myco-registry-password-value-base64
I want that this values replace env variables in my .npmrc on the install action. But when I try npm install I get an error: Error: Failed to replace env in config: ${MYCO_REGISTRY_USER}. I can understand why it happens. Possibly because npm reads .npmrc values first and try to replace env variables and fails, because in this moment it know nothing about dotenv.
My question is how to deal with it?
Short summary:
I do not want to keep env variables in the terminal profile, instead I want to put it in the .env file inside my project.
I have to replace env variables in the .npmrc file with dotenv before npm install
I know this answer might come too late, but in case anyone else is looking for answers, here's a solution:
You need to prepend your scripts with dotenv-cli as so:
dotenv npm install
or in my case where the file was not .env:
dotenv -e .env.local npm install
The problem is that you cannot save this anywhere so that someone can use it with "npm install" somehow. Definitely npm preinstall is run after reading .npmrc so it fails too.
You will need to either document it well or just include a small shell script, but if you're supporting different OSs then it can get funny really fast...
Happily so, CD platforms like Netlify allow you to set environment variables manually.
But I guess this must not be the nicest of starts if someone clones your repo and the first they've got is a failing npm install 🤷‍♂️
Also, check this one out: locking-the-vault-on-font-awesome-npm-tokens

NodeJS: add options to npmrc

I am currently running my script as
node --no-deprecation main.js
I was wondering if there is a way to add this option to ~/.npmrc or otherwise (environment variable), such that I do not need to add the option to the command line. I would like to just run my script as node main.js and to be shown no deprecation warnings.
To make this question more general, is there a general way in NodeJS of setting flag values in a config file rather than adding it to the command (say I am interested in running with --trace-warnings as well.
I am looking for something cleaner than say alias node="node --no-deprecation".
I already tried adding this line inside ~/.npmrc
no-deprecationss=true
Reference:
https://nodejs.org/dist/latest-v7.x/docs/api/cli.html#cli_no_deprecation
If you're looking for a global configuration, setting the --no-deprecation flag in the NODE_OPTIONS environment variable in your .bashrc or other shell config file is what you're looking for, as long as you're using node v8 or above.
https://nodejs.org/docs/latest-v10.x/api/cli.html#cli_node_options_options
You can do it via package.json. e.g
"scripts": {
"main": "node --no-deprecation main.js"
},
and run it via
npm run main
or
yarn main
I know this is an old question, but for anyone new who comes across it, one reproducible way to do this without having to add it to every npm script and without affecting other projects (which is what would happen if you added it to .bashrc or .profile), is to add a .npmrc file in your project with node-options set in it.
It accepts the same flags as NODE_OPTIONS except applied to every npm script in your project without affecting others and keeping your scripts DRY.
e.g.
# Sets the flags in NODE_OPTIONS when running `npm run myScript`
node-options='--no-deprecation'

set custom PATH for npm

Is there a way that I can add a directory to the PATH of npm. I DON'T want to add this directory to the machine PATH, just the one npm uses when running scripts.
I know that npm adds node_modules/.bin in addition to any pre-existing machine PATH (see here)
To give more detail on my specific case. I have a project with nested directories, each with its own package.json. When running a script on a sub-directory which depends on a parent binary, the binary won't be found because it's not on the local node_modules/.bin but inside a parent node_modules/.bin.
I could specify the path to the binary inside the script but this is cumbersome and makes the scripts less readable.
So, is there a way to tell npm to export PATH before running every script? It's already doing something like this to add the local node_modules/.bin
I can't think of any simple way to accomplish what you are describing.
You can set/modify environment variables right before a script like:
{
"scripts": {
"parent-script": "PATH=$PATH:/path/to/parent/node_modules/.bin parent-script"
}
}
But as you mentioned this is cumbersome to do this on every script. Also you might as well just do as you described:
{
"scripts": {
"parent-script": "/path/to/parent/node_modules/.bin/parent-script"
}
}
A complicated, but possibly more maintainable approach could be to build yourself a search-script Node module that will traverse parent directories for a script passed as an argument, then run it:
{
"dependencies": {
"search-script": "^0.0.1"
},
"scripts": {
"parent-script": "search-script parent-script"
}
}
Unfortunately NPM does not provide a lot of flexibility for things like this.
Only thing I can see is creating a shell script called npm, place it in a folder in your PATH and remove npm from your PATH and have your shell script set the parent binary dir in your PATH and call the npm binary passing the rest of args. It's not really worth it and might cause other issues.
I would just add all those binaries your scripts in nested folders depend on in their own package.json, which in some ways is worth it, specially if you want to deploy them independently.
This is a node/npm issue itself as it kind of forces you to download the same package multiple times, but at least it's easy to know which version your package uses.

NPM - Conditional additions to global path

In a Node package.json file, you can map multiple executables to the PATH environmental variable on a global NPM install (npm install -g):
"bin": {
"foo": "./bin/foo.js",
"bar": "./bin/bar.js"
},
I have a unique project that requires mapping existing PATH variables on Operating Systems that do not have it. For example, I want to add a command named grep to PATH, if and only if it is being installed on a Windows computer. If the computer is running any other OS, the NPM installation will obviously fail.
Is there any way to run logic that pre-determines what bin options are available in the installation?
Oh snap - I just had an idea!
Would this work:
Parent module has npm (programmatic version) as a dependency.
On global installation, run a post-install script as declared in the package.json of parent module.
Post-install script does a check on the system to see which commands exist. This would be more mature than "Windows or not Windows" - it would try to exec a list of commands and see which ones fail.
For every command that doesn't exist, post-install script programmatically runs npm install -g on all sub-modules (one for each command, such as grep).
This would take a while and the npm module is huge, but it seems like it would work. No?
There doesn't seem to be a way to do this directly through package.json, but it might be possible (and desirable) to do something like:
Make a separate npm module for each executable you want to register (eg my-win-grep).
In the my-win-grep module, implement the executable code you want to run, and register the PATH/executable value in this module.
In the package.json for my-win-grep, include an os field that limits it to installing on windows.
In your original module, list my-win-grep as an optionalDependency.
In this way, if someone installs your original module on Windows, it would install my-win-grep, which would register an executable to run under the grep command.
For users on other systems, the my-win-grep module would not install because of the os requirement, but the main module would continue to install because it ignores failures under optionalDependencies. So the original grep executable would remain untouched.
Updated question
This approach does sound like it should work - as you say, the npm dependency is pretty large, but it does avoid having to preform additional symlinking, and still has the benefit outlined above of having each piece of OS specific functionality in a separate module.
The only thing to watch for, possibly, in the programmatic npm docs:
You cannot set configs individually for any single npm function at
this time. Since npm is a singleton, any call to npm.config.set will
change the value for all npm commands in that process
So this might just mean that you can't specify -g on your installs, but instead would have to set it globally before the first install. This shouldn't be a problem, but you'll probably need to test it out to find out exactly.
Lastly...
You might also want to have a look at https://github.com/lastboy/package-script - even if you don't use it, it might give you some inspiration for your implementation.

Resources