This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

ci: add new npm ci installer

  • Loading branch information...
zkat authored and iarna committed Jan 8, 2018
1 parent 26cd648 commit 5e4de9c99c934e25ef7b9c788244cc3c993da559
View
@@ -0,0 +1,60 @@
npm-ci(1) -- Install a project with a clean slate
===================================
## SYNOPSIS
npm ci
## EXAMPLE
Make sure you have a package-lock and an up-to-date install:
```
$ cd ./my/npm/project
$ npm install
added 154 packages in 10s
$ ls | grep package-lock
```
Run `npm ci` in that project
```
$ npm ci
added 154 packages in 5s
```
Configure Travis to build using `npm ci` instead of `npm install`:
```
# .travis.yml
install:
- npm ci
# keep the npm cache around to speed up installs
cache:
directories:
- "$HOME/.npm"
```
## DESCRIPTION
This command is similar to `npm-install(1)`, except it's meant to be used in
automated environments such as test platforms, continuous integration, and
deployment. It can be significantly faster than a regular npm install by
skipping certain user-oriented features. It is also more strict than a regular
install, which can help catch errors or inconsistencies caused by the
incrementally-installed local environments of most npm users.
Concretely, the main differences between an `npm install` call and an `npm ci`
call are:
* The resulting `node_modules/` is not meant for interactive development.
* The project **must** have an existing `package-lock.json` or `npm-shrinkwrap.json`.
* If dependencies in the package lock do not match those in `package.json`, `npm ci` will exit with an error, instead of updating the package lock.
* `npm ci` can only install entire projects at a time: individual dependencies cannot be added with this command.
* If a `node_modules` is already present, it will be automatically removed before `npm ci` begins its install.
* It will never write to `package.json` or any of the package-locks: installs are essentially frozen.
## SEE ALSO
* npm-install(1)
* npm-package-locks(5)
View
@@ -0,0 +1,37 @@
'use strict'
const Installer = require('libcipm')
const lifecycleOpts = require('./config/lifecycle.js')
const npm = require('./npm.js')
const npmlog = require('npmlog')
const pacoteOpts = require('./config/pacote.js')
ci.usage = 'npm ci'
ci.completion = (cb) => cb(null, [])
Installer.CipmConfig.impl(npm.config, {
get: npm.config.get,
set: npm.config.set,
toLifecycle (moreOpts) {
return lifecycleOpts(moreOpts)
},
toPacote (moreOpts) {
return pacoteOpts(moreOpts)
}
})
module.exports = ci
function ci (args, cb) {
return new Installer({
config: npm.config,
log: npmlog
})
.run()
.then(
(details) => console.error(`added ${details.pkgCount} packages in ${
details.runTime / 1000
}s`)
)
.then(() => cb(), cb)
}
View
@@ -22,6 +22,7 @@ var affordances = {
'la': 'ls',
'll': 'ls',
'verison': 'version',
'ic': 'ci',
'isntall': 'install',
'dist-tags': 'dist-tag',
'apihelp': 'help',
@@ -46,6 +47,7 @@ var affordances = {
// these are filenames in .
var cmdList = [
'ci',
'install',
'install-test',
'uninstall',
@@ -10,9 +10,9 @@ module.exports = (args, cb) => {
const spec = parsed[0]
const extractTo = parsed[1]
const opts = parsed[2]
if (!opts.log && opts.loglevel) {
if (!opts.log) {
opts.log = npmlog
opts.log.level = opts.loglevel
}
opts.log.level = opts.loglevel || opts.log.level
BB.resolve(extract(spec, extractTo, opts)).nodeify(cb)
}
@@ -57,12 +57,11 @@ function extract (staging, pkg, log) {
pacoteOpts = require('../../config/pacote')
}
const opts = pacoteOpts({
integrity: pkg.package._integrity
integrity: pkg.package._integrity,
resolved: pkg.package._resolved
})
const args = [
pkg.package._resolved
? npa.resolve(pkg.package.name, pkg.package._resolved)
: pkg.package._requested,
pkg.package._requested,
extractTo,
opts
]
@@ -14,7 +14,6 @@ const realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js')
const validate = require('aproba')
const path = require('path')
const isRegistry = require('../utils/is-registry.js')
const url = require('url')
const hasModernMeta = require('./has-modern-meta.js')
module.exports = function (tree, sw, opts, finishInflating) {
@@ -108,7 +107,7 @@ function makeFakeChild (name, topPath, tree, sw, requested) {
name: name,
version: sw.version,
_id: name + '@' + sw.version,
_resolved: adaptResolved(requested, sw.resolved),
_resolved: sw.resolved,
_requested: requested,
_optional: sw.optional,
_development: sw.dev,
@@ -146,31 +145,6 @@ function makeFakeChild (name, topPath, tree, sw, requested) {
return child
}
function isFile (href) {
try {
return url.parse(href).protocol === 'file:'
} catch (_) {
return false
}
}
function adaptResolved (requested, resolved) {
const registry = requested.scope
? npm.config.get(`${requested.scope}:registry`) || npm.config.get('registry')
: npm.config.get('registry')
if (!isRegistry(requested) || (resolved && resolved.indexOf(registry) === 0) || isFile(resolved)) {
// Nothing to worry about here. Pass it through.
return resolved
} else {
// We could fast-path for registry.npmjs.org here, but if we do, it
// would end up getting written back to the `resolved` field. By always
// returning `null` for other registries, `pacote.extract()` will take
// care of any required metadata fetches internally, without altering
// the tree we're going to write out to shrinkwrap/lockfile.
return null
}
}
function fetchChild (topPath, tree, sw, requested) {
return fetchPackageMetadata(requested, topPath).then((pkg) => {
pkg._from = sw.from || requested.raw
Oops, something went wrong.

2 comments on commit 5e4de9c

@branpar

This comment has been minimized.

branpar replied Mar 6, 2018

Will npm ci have any conflict if I already have a ci script in my package.json file?

@prashantpalikhe

This comment has been minimized.

prashantpalikhe replied Mar 7, 2018

You run your ci script with npm run ci isntead of npm ci. So should be no conflict.

Please sign in to comment.