Skip to content

Commit

Permalink
install: add --before date support for time traveling~
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Feb 13, 2019
1 parent dd7098a commit 50b63fc
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 2 deletions.
16 changes: 16 additions & 0 deletions doc/misc/npm-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,22 @@ If set to true, then npm will stubbornly refuse to install (or even
consider installing) any package that claims to not be compatible with
the current Node.js version.

### enjoy-by

* Alias: before
* Default: null
* Type: Date

If passed to `npm install`, will rebuild the npm tree such that only versions
that were available **on or before** the `enjoy-by` time get installed.
If there's no versions available for the current set of direct dependencies, the
command will error.

If the requested version is a `dist-tag` and the given tag does not pass the
`enjoy-by` filter, the most recent version less than or equal to that tag will
be used. For example, `foo@latest` might install `foo@1.2` even though `latest`
is `2.0`.

### force

* Default: false
Expand Down
3 changes: 3 additions & 0 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Object.defineProperty(exports, 'defaults', {get: function () {
'dry-run': false,
editor: osenv.editor(),
'engine-strict': false,
'enjoy-by': null,
force: false,

'fetch-retries': 2,
Expand Down Expand Up @@ -279,6 +280,7 @@ exports.types = {
'dry-run': Boolean,
editor: String,
'engine-strict': Boolean,
'enjoy-by': [null, Date],
force: Boolean,
'fetch-retries': Number,
'fetch-retry-factor': Number,
Expand Down Expand Up @@ -394,6 +396,7 @@ function getLocalAddresses () {
}

exports.shorthands = {
before: ['--enjoy-by'],
s: ['--loglevel', 'silent'],
d: ['--loglevel', 'info'],
dd: ['--loglevel', 'verbose'],
Expand Down
21 changes: 19 additions & 2 deletions lib/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,25 @@ Installer.prototype.cloneCurrentTreeToIdealTree = function (cb) {
validate('F', arguments)
log.silly('install', 'cloneCurrentTreeToIdealTree')

this.idealTree = copyTree(this.currentTree)
this.idealTree.warnings = []
if (npm.config.get('enjoy-by')) {
this.idealTree = {
package: this.currentTree.package,
path: this.currentTree.path,
realpath: this.currentTree.realpath,
children: [],
requires: [],
missingDeps: {},
missingDevDeps: {},
requiredBy: [],
error: this.currentTree.error,
warnings: [],
isTop: true
}
} else {
this.idealTree = copyTree(this.currentTree)
this.idealTree.warnings = []
}

cb()
}

Expand Down
89 changes: 89 additions & 0 deletions test/tap/install-before.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

const BB = require('bluebird')

const common = require('../common-tap.js')
const mockTar = require('../util/mock-tarball.js')
const mr = common.fakeRegistry.compat
const path = require('path')
const rimraf = BB.promisify(require('rimraf'))
const Tacks = require('tacks')
const { test } = require('tap')

const { Dir, File } = Tacks

const testDir = path.join(__dirname, path.basename(__filename, '.js'))

let server
test('setup', t => {
mr({}, (err, s) => {
t.ifError(err, 'registry mocked successfully')
server = s
t.end()
})
})

test('installs an npm package before a certain date', t => {
const fixture = new Tacks(Dir({
'package.json': File({})
}))
fixture.create(testDir)
const packument = {
name: 'foo',
'dist-tags': { latest: '1.2.4' },
versions: {
'1.2.3': {
name: 'foo',
version: '1.2.3',
dist: {
tarball: `${server.registry}/foo/-/foo-1.2.3.tgz`
}
},
'1.2.4': {
name: 'foo',
version: '1.2.4',
dist: {
tarball: `${server.registry}/foo/-/foo-1.2.4.tgz`
}
}
},
time: {
created: '2017-01-01T00:00:01.000Z',
modified: '2018-01-01T00:00:01.000Z',
'1.2.3': '2017-01-01T00:00:01.000Z',
'1.2.4': '2018-01-01T00:00:01.000Z'
}
}
server.get('/foo').reply(200, packument)
return mockTar({
'package.json': JSON.stringify({
name: 'foo',
version: '1.2.3'
})
}).then(tarball => {
server.get('/foo/-/foo-1.2.3.tgz').reply(200, tarball)
server.get('/foo/-/foo-1.2.4.tgz').reply(500)
return common.npm([
'install', 'foo',
'--before', '2018',
'--json',
'--cache', path.join(testDir, 'npmcache'),
'--registry', server.registry
], { cwd: testDir })
}).then(([code, stdout, stderr]) => {
t.comment(stdout)
t.comment(stderr)
t.like(JSON.parse(stdout), {
added: [{
action: 'add',
name: 'foo',
version: '1.2.3'
}]
}, 'installed the 2017 version of the package')
})
})

test('cleanup', t => {
server.close()
return rimraf(testDir)
})

0 comments on commit 50b63fc

Please sign in to comment.