Skip to content
This repository has been archived by the owner on May 10, 2021. It is now read-only.

Commit

Permalink
feat(impl): rough prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed May 29, 2017
1 parent 36e363a commit 2970e43
Show file tree
Hide file tree
Showing 11 changed files with 4,267 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/.nyc_output
/test/cache
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: node_js
sudo: false
node_js:
- "7"
- "6"
- "4"
7 changes: 7 additions & 0 deletions ISSUE_TEMPLATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--
⚠️🚨 BEFORE FILING AN ISSUE: 🚨⚠️

👉🏼 CONTRIBUTING.md 👈🏼 (the "contribution guidelines" up there ☝🏼)

I PROMISE IT'S A VERY VERY SHORT READ.🙇🏼
-->
3 changes: 3 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
To the extent possible under law, maintainers for this project have waived all copyright and related or neighboring rights to this project.

For more information on this waiver, see: https://creativecommons.org/publicdomain/zero/1.0/
7 changes: 7 additions & 0 deletions PULL_REQUEST_TEMPLATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--
⚠️🚨 BEFORE FILING A PR: 🚨⚠️

👉🏼 CONTRIBUTING.md 👈🏼 (the "contribution guidelines" up there ☝🏼)

I PROMISE IT'S A VERY VERY SHORT READ.🙇🏼
-->
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[![npm](https://img.shields.io/npm/v/cipm.svg)](https://npm.im/cipm) [![license](https://img.shields.io/npm/l/cipm.svg)](https://npm.im/cipm) [![Travis](https://img.shields.io/travis/zkat/cipm.svg)](https://travis-ci.org/zkat/cipm) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/cipm?svg=true)](https://ci.appveyor.com/project/zkat/cipm) [![Coverage Status](https://coveralls.io/repos/github/zkat/cipm/badge.svg?branch=latest)](https://coveralls.io/github/zkat/cipm?branch=latest)

## NOTE: this project is under active development. Please don't use it yet.

# cipm(1) -- install npm dependencies from a package lock

## SYNOPSIS

`cipm [--userconfig <path>] [--ignore-scripts] [--offline] [--loglevel <level>]`

## INSTALL

`npm install [-g|-D] cipm`

## DESCRIPTION

When invoked inside an npm project with a `package.json` and `package-lock.json` (or an `npm-shrinwkrap.json`), it will install the specified dependencies and run their install scripts.

The main difference between this and `npm install` is that `cipm` is both a small, standalone program, and that it can bypass a lot of the heavier machinery in npm oriented towards interacting with invalid states: `cipm` completely removes `node_modules` before beginning the install, if it exists.

`cipm` also requires that the current project have an existing lockfile, which must first be generated using `npm install` in `npm@5` or later versions (or any other package manager supporting `lockfileVersion@>=1`).

This tool is ideal for using in CI environments that require regular, full installs of an application, but that are usually able to cache package data in a central cache.

## EXAMPLES

## AUTHOR

Written by [Kat Marchan](https://github.com/zkat).

## REPORTING BUGS

Please file any relevant issues [on Github.](https://github.com/zkat/cipm)

## LICENSE

This work is released by its authors into the public domain under CC0-1.0. See `LICENSE.md` for details.

## SEE ALSO

* `npm-install(1)`
* `npm-package-locks(5)`
* `package-lock.json(5)`
22 changes: 22 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
environment:
matrix:
- nodejs_version: "7"
- nodejs_version: "6"
- nodejs_version: "4"

platform:
- x64

install:
- ps: Install-Product node $env:nodejs_version $env:platform
- npm config set spin false
- npm i -g npm@latest
- npm install

test_script:
- npm test

matrix:
fast_finish: true

build: off
155 changes: 155 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
'use strict'

const BB = require('bluebird')

const extractionWorker = require('./worker.js')
const fs = BB.promisifyAll(require('graceful-fs'))
const npa = require('npm-package-arg')
const path = require('path')
const rimraf = BB.promisify(require('rimraf'))
const workerFarm = require('worker-farm')
const yargs = require('yargs')

const WORKER_PATH = require.resolve('./worker.js')
let workers

main(parseArgs())

let pkgCount = 0

function parseArgs () {
return yargs
.option('loglevel', {
type: 'string',
describe: 'log level for npmlog',
default: 'notice'
})
.option('offline', {
type: 'boolean',
describe: 'force cipm to run offline, or error'
})
.option('ignore-scripts', {
type: 'boolean',
describe: 'skip running lifecycle scripts'
})
.option('userconfig', {
type: 'string',
describe: 'path to npmrc'
})
.argv
}

function main () {
const startTime = Date.now()
workers = workerFarm({
maxConcurrentCallsPerWorker: 20,
maxRetries: 1
}, WORKER_PATH)
return BB.join(
readJson('.', 'package.json'),
readJson('.', 'package-lock.json', true),
readJson('.', 'npm-shrinkwrap.json', true),
(pkg, lock, shrink) => {
pkg._shrinkwrap = lock || shrink
return pkg
}
).tap(pkg => {
if (!pkg._shrinkwrap) {
throw new Error(`cipm can only install packages with an existing package-lock.json or npm-shrinkwrap.json. Run an install with npm@5 or later to generate it, then try again.`)
}

return rimraf('./node_modules')
}).tap(pkg => {
return runScript('preinstall', pkg, '.')
}).tap(pkg => {
return extractDeps(
`./node_modules`,
pkg._shrinkwrap.dependencies
)
}).tap(pkg => {
return runScript('install', pkg, '.')
}).tap(pkg => {
return runScript('postinstall', pkg, '.')
}).then(pkg => {
workerFarm.end(workers)
console.log(`added ${pkgCount} packages in ${
(Date.now() - startTime) / 1000
}s.`)
})
}

function runScript (script, pkg, pkgPath) {
if (pkg.scripts && pkg.scripts[script]) {
console.log('executing', script, 'on', pkgPath)
}
return BB.resolve()
}

function extractDeps (modPath, deps) {
return BB.map(Object.keys(deps || {}), (name) => {
const child = deps[name]
const childPath = path.join(modPath, name)
return (
child.bundled
? BB.resolve()
: extractChild(name, child, childPath)
).then(() => {
return readJson(childPath, 'package.json')
}).tap(pkg => {
return runScript('preinstall', pkg, childPath)
}).then(pkg => {
return extractDeps(path.join(childPath, 'node_modules'))
.then(dependencies => {
return {
name,
package: pkg,
child,
childPath,
dependencies: dependencies.reduce((acc, dep) => {
acc[dep.name] = dep
return acc
}, {})
}
})
}).tap(full => {
pkgCount++
return runScript('install', full.package, childPath)
}).tap(full => {
return runScript('postinstall', full.package, childPath)
})
})
}

function extractChild (name, child, childPath) {
const spec = npa.resolve(name, child.resolved || child.version)
const opts = {
cache: path.resolve(process.env.HOME, '.npm/_cacache'),
integrity: child.integrity
}
const args = [spec, childPath, opts]
return BB.fromNode((cb) => {
let launcher = extractionWorker
let msg = args
const spec = typeof args[0] === 'string' ? npa(args[0]) : args[0]
if (spec.registry || spec.type === 'remote') {
// workers will run things in parallel!
launcher = workers
try {
msg = JSON.stringify(msg)
} catch (e) {
return cb(e)
}
}
launcher(msg, cb)
})
}

function readJson (jsonPath, name, ignoreMissing) {
return fs.readFileAsync(path.join(jsonPath, name), 'utf8')
.then(str => JSON.parse(str))
.catch({code: 'ENOENT'}, err => {
if (!ignoreMissing) {
throw err
}
})
}
Loading

0 comments on commit 2970e43

Please sign in to comment.