mirror of
https://github.com/shimataro/ssh-key-action.git
synced 2025-06-19 22:52:10 +10:00
255 lines
8.5 KiB
JavaScript
255 lines
8.5 KiB
JavaScript
'use strict'
|
|
|
|
const BB = require('bluebird')
|
|
|
|
const cacache = require('cacache')
|
|
const cacheKey = require('./util/cache-key')
|
|
const fetchFromManifest = require('./fetch').fromManifest
|
|
const finished = require('./util/finished')
|
|
const minimatch = require('minimatch')
|
|
const normalize = require('normalize-package-data')
|
|
const optCheck = require('./util/opt-check')
|
|
const path = require('path')
|
|
const pipe = BB.promisify(require('mississippi').pipe)
|
|
const ssri = require('ssri')
|
|
const tar = require('tar')
|
|
const readJson = require('./util/read-json')
|
|
|
|
// `finalizeManifest` takes as input the various kinds of manifests that
|
|
// manifest handlers ('lib/fetchers/*.js#manifest()') return, and makes sure
|
|
// they are:
|
|
//
|
|
// * filled out with any required data that the handler couldn't fill in
|
|
// * formatted consistently
|
|
// * cached so we don't have to repeat this work more than necessary
|
|
//
|
|
// The biggest thing this package might do is do a full tarball extraction in
|
|
// order to find missing bits of metadata required by the npm installer. For
|
|
// example, it will fill in `_shrinkwrap`, `_integrity`, and other details that
|
|
// the plain manifest handlers would require a tarball to fill out. If a
|
|
// handler returns everything necessary, this process is skipped.
|
|
//
|
|
// If we get to the tarball phase, the corresponding tarball handler for the
|
|
// requested type will be invoked and the entire tarball will be read from the
|
|
// stream.
|
|
//
|
|
module.exports = finalizeManifest
|
|
function finalizeManifest (pkg, spec, opts) {
|
|
const key = finalKey(pkg, spec)
|
|
opts = optCheck(opts)
|
|
|
|
const cachedManifest = (opts.cache && key && !opts.preferOnline && !opts.fullMetadata && !opts.enjoyBy)
|
|
? cacache.get.info(opts.cache, key, opts)
|
|
: BB.resolve(null)
|
|
|
|
return cachedManifest.then(cached => {
|
|
if (cached && cached.metadata && cached.metadata.manifest) {
|
|
return new Manifest(cached.metadata.manifest)
|
|
} else {
|
|
return tarballedProps(pkg, spec, opts).then(props => {
|
|
return pkg && pkg.name
|
|
? new Manifest(pkg, props, opts.fullMetadata)
|
|
: new Manifest(props, null, opts.fullMetadata)
|
|
}).then(manifest => {
|
|
const cacheKey = key || finalKey(manifest, spec)
|
|
if (!opts.cache || !cacheKey) {
|
|
return manifest
|
|
} else {
|
|
return cacache.put(
|
|
opts.cache, cacheKey, '.', {
|
|
metadata: {
|
|
id: manifest._id,
|
|
manifest,
|
|
type: 'finalized-manifest'
|
|
}
|
|
}
|
|
).then(() => manifest)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
module.exports.Manifest = Manifest
|
|
function Manifest (pkg, fromTarball, fullMetadata) {
|
|
fromTarball = fromTarball || {}
|
|
if (fullMetadata) {
|
|
Object.assign(this, pkg)
|
|
}
|
|
this.name = pkg.name
|
|
this.version = pkg.version
|
|
this.engines = pkg.engines || fromTarball.engines
|
|
this.cpu = pkg.cpu || fromTarball.cpu
|
|
this.os = pkg.os || fromTarball.os
|
|
this.dependencies = pkg.dependencies || {}
|
|
this.optionalDependencies = pkg.optionalDependencies || {}
|
|
this.devDependencies = pkg.devDependencies || {}
|
|
const bundled = (
|
|
pkg.bundledDependencies ||
|
|
pkg.bundleDependencies ||
|
|
false
|
|
)
|
|
this.bundleDependencies = bundled
|
|
this.peerDependencies = pkg.peerDependencies || {}
|
|
this.deprecated = pkg.deprecated || false
|
|
|
|
// These depend entirely on each handler
|
|
this._resolved = pkg._resolved
|
|
|
|
// Not all handlers (or registries) provide these out of the box,
|
|
// and if they don't, we need to extract and read the tarball ourselves.
|
|
// These are details required by the installer.
|
|
this._integrity = pkg._integrity || fromTarball._integrity || null
|
|
this._shasum = pkg._shasum || fromTarball._shasum || null
|
|
this._shrinkwrap = pkg._shrinkwrap || fromTarball._shrinkwrap || null
|
|
this.bin = pkg.bin || fromTarball.bin || null
|
|
|
|
if (this.bin && Array.isArray(this.bin)) {
|
|
// Code yanked from read-package-json.
|
|
const m = (pkg.directories && pkg.directories.bin) || '.'
|
|
this.bin = this.bin.reduce((acc, mf) => {
|
|
if (mf && mf.charAt(0) !== '.') {
|
|
const f = path.basename(mf)
|
|
acc[f] = path.join(m, mf)
|
|
}
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
this._id = null
|
|
|
|
// TODO - freezing and inextensibility pending npm changes. See test suite.
|
|
// Object.preventExtensions(this)
|
|
normalize(this)
|
|
|
|
// I don't want this why did you give it to me. Go away. 🔥🔥🔥🔥
|
|
delete this.readme
|
|
|
|
// Object.freeze(this)
|
|
}
|
|
|
|
// Some things aren't filled in by standard manifest fetching.
|
|
// If this function needs to do its work, it will grab the
|
|
// package tarball, extract it, and take whatever it needs
|
|
// from the stream.
|
|
function tarballedProps (pkg, spec, opts) {
|
|
const needsShrinkwrap = (!pkg || (
|
|
pkg._hasShrinkwrap !== false &&
|
|
!pkg._shrinkwrap
|
|
))
|
|
const needsBin = !!(!pkg || (
|
|
!pkg.bin &&
|
|
pkg.directories &&
|
|
pkg.directories.bin
|
|
))
|
|
const needsIntegrity = !pkg || (!pkg._integrity && pkg._integrity !== false)
|
|
const needsShasum = !pkg || (!pkg._shasum && pkg._shasum !== false)
|
|
const needsHash = needsIntegrity || needsShasum
|
|
const needsManifest = !pkg || !pkg.name
|
|
const needsExtract = needsShrinkwrap || needsBin || needsManifest
|
|
if (!needsShrinkwrap && !needsBin && !needsHash && !needsManifest) {
|
|
return BB.resolve({})
|
|
} else {
|
|
opts = optCheck(opts)
|
|
const tarStream = fetchFromManifest(pkg, spec, opts)
|
|
const extracted = needsExtract && new tar.Parse()
|
|
return BB.join(
|
|
needsShrinkwrap && jsonFromStream('npm-shrinkwrap.json', extracted),
|
|
needsManifest && jsonFromStream('package.json', extracted),
|
|
needsBin && getPaths(extracted),
|
|
needsHash && ssri.fromStream(tarStream, { algorithms: ['sha1', 'sha512'] }),
|
|
needsExtract && pipe(tarStream, extracted),
|
|
(sr, mani, paths, hash) => {
|
|
if (needsManifest && !mani) {
|
|
const err = new Error(`Non-registry package missing package.json: ${spec}.`)
|
|
err.code = 'ENOPACKAGEJSON'
|
|
throw err
|
|
}
|
|
const extraProps = mani || {}
|
|
delete extraProps._resolved
|
|
// drain out the rest of the tarball
|
|
tarStream.resume()
|
|
// if we have directories.bin, we need to collect any matching files
|
|
// to add to bin
|
|
if (paths && paths.length) {
|
|
const dirBin = mani
|
|
? (mani && mani.directories && mani.directories.bin)
|
|
: (pkg && pkg.directories && pkg.directories.bin)
|
|
if (dirBin) {
|
|
extraProps.bin = {}
|
|
paths.forEach(filePath => {
|
|
if (minimatch(filePath, dirBin + '/**')) {
|
|
const relative = path.relative(dirBin, filePath)
|
|
if (relative && relative[0] !== '.') {
|
|
extraProps.bin[path.basename(relative)] = path.join(dirBin, relative)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
return Object.assign(extraProps, {
|
|
_shrinkwrap: sr,
|
|
_resolved: (mani && mani._resolved) ||
|
|
(pkg && pkg._resolved) ||
|
|
spec.fetchSpec,
|
|
_integrity: needsIntegrity && hash && hash.sha512 && hash.sha512[0].toString(),
|
|
_shasum: needsShasum && hash && hash.sha1 && hash.sha1[0].hexDigest()
|
|
})
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
function jsonFromStream (filename, dataStream) {
|
|
return BB.fromNode(cb => {
|
|
dataStream.on('error', cb)
|
|
dataStream.on('close', cb)
|
|
dataStream.on('entry', entry => {
|
|
const filePath = entry.header.path.replace(/[^/]+\//, '')
|
|
if (filePath !== filename) {
|
|
entry.resume()
|
|
} else {
|
|
let data = ''
|
|
entry.on('error', cb)
|
|
finished(entry).then(() => {
|
|
try {
|
|
cb(null, readJson(data))
|
|
} catch (err) {
|
|
cb(err)
|
|
}
|
|
}, err => {
|
|
cb(err)
|
|
})
|
|
entry.on('data', d => { data += d })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
function getPaths (dataStream) {
|
|
return BB.fromNode(cb => {
|
|
let paths = []
|
|
dataStream.on('error', cb)
|
|
dataStream.on('close', () => cb(null, paths))
|
|
dataStream.on('entry', function handler (entry) {
|
|
const filePath = entry.header.path.replace(/[^/]+\//, '')
|
|
entry.resume()
|
|
paths.push(filePath)
|
|
})
|
|
})
|
|
}
|
|
|
|
function finalKey (pkg, spec) {
|
|
if (pkg && pkg._uniqueResolved) {
|
|
// git packages have a unique, identifiable id, but no tar sha
|
|
return cacheKey(`${spec.type}-manifest`, pkg._uniqueResolved)
|
|
} else {
|
|
return (
|
|
pkg && pkg._integrity &&
|
|
cacheKey(
|
|
`${spec.type}-manifest`,
|
|
`${pkg._resolved}:${ssri.stringify(pkg._integrity)}`
|
|
)
|
|
)
|
|
}
|
|
}
|