| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- 'use strict'
- const BB = require('bluebird')
- const cacache = require('cacache')
- const cacheKey = require('../util/cache-key')
- const Fetcher = require('../fetch')
- const git = require('../util/git')
- const mkdirp = BB.promisify(require('mkdirp'))
- const pickManifest = require('npm-pick-manifest')
- const optCheck = require('../util/opt-check')
- const osenv = require('osenv')
- const packDir = require('../util/pack-dir')
- const PassThrough = require('stream').PassThrough
- const path = require('path')
- const pipe = BB.promisify(require('mississippi').pipe)
- const rimraf = BB.promisify(require('rimraf'))
- const uniqueFilename = require('unique-filename')
- // `git` dependencies are fetched from git repositories and packed up.
- const fetchGit = module.exports = Object.create(null)
- Fetcher.impl(fetchGit, {
- packument (spec, opts) {
- return BB.reject(new Error('Not implemented yet.'))
- },
- manifest (spec, opts) {
- opts = optCheck(opts)
- if (spec.hosted && spec.hosted.getDefaultRepresentation() === 'shortcut') {
- return hostedManifest(spec, opts)
- } else {
- // If it's not a shortcut, don't do fallbacks.
- return plainManifest(spec.fetchSpec, spec, opts)
- }
- },
- tarball (spec, opts) {
- opts = optCheck(opts)
- const stream = new PassThrough()
- this.manifest(spec, opts).then(manifest => {
- stream.emit('manifest', manifest)
- return pipe(
- this.fromManifest(
- manifest, spec, opts
- ).on('integrity', i => stream.emit('integrity', i)), stream
- )
- }).catch(err => stream.emit('error', err))
- return stream
- },
- fromManifest (manifest, spec, opts) {
- opts = optCheck(opts)
- let streamError
- const stream = new PassThrough().on('error', e => { streamError = e })
- const cacheName = manifest._uniqueResolved || manifest._resolved || ''
- const cacheStream = (
- opts.cache &&
- cacache.get.stream(
- opts.cache, cacheKey('packed-dir', cacheName), opts
- ).on('integrity', i => stream.emit('integrity', i))
- )
- cacheStream.pipe(stream)
- cacheStream.on('error', err => {
- if (err.code !== 'ENOENT') {
- return stream.emit('error', err)
- } else {
- stream.emit('reset')
- return withTmp(opts, tmp => {
- if (streamError) { throw streamError }
- return cloneRepo(
- spec, manifest._repo, manifest._ref, manifest._rawRef, tmp, opts
- ).then(HEAD => {
- if (streamError) { throw streamError }
- manifest._resolved = spec.saveSpec.replace(/(:?#.*)?$/, `#${HEAD}`)
- manifest._uniqueResolved = manifest._resolved
- return packDir(manifest, manifest._uniqueResolved, tmp, stream, opts)
- })
- }).catch(err => stream.emit('error', err))
- }
- })
- return stream
- }
- })
- function hostedManifest (spec, opts) {
- return BB.resolve(null).then(() => {
- if (!spec.hosted.git()) {
- throw new Error(`No git url for ${spec}`)
- }
- return plainManifest(spec.hosted.git(), spec, opts)
- }).catch(err => {
- if (!spec.hosted.https()) {
- throw err
- }
- return plainManifest(spec.hosted.https(), spec, opts)
- }).catch(err => {
- if (!spec.hosted.sshurl()) {
- throw err
- }
- return plainManifest(spec.hosted.sshurl(), spec, opts)
- })
- }
- function plainManifest (repo, spec, opts) {
- const rawRef = spec.gitCommittish || spec.gitRange
- return resolve(
- repo, spec, spec.name, opts
- ).then(ref => {
- if (ref) {
- const resolved = spec.saveSpec.replace(/(?:#.*)?$/, `#${ref.sha}`)
- return {
- _repo: repo,
- _resolved: resolved,
- _spec: spec,
- _ref: ref,
- _rawRef: spec.gitCommittish || spec.gitRange,
- _uniqueResolved: resolved,
- _integrity: false,
- _shasum: false
- }
- } else {
- // We're SOL and need a full clone :(
- //
- // If we're confident enough that `rawRef` is a commit SHA,
- // then we can at least get `finalize-manifest` to cache its result.
- const resolved = spec.saveSpec.replace(/(?:#.*)?$/, rawRef ? `#${rawRef}` : '')
- return {
- _repo: repo,
- _rawRef: rawRef,
- _resolved: rawRef && rawRef.match(/^[a-f0-9]{40}$/) && resolved,
- _uniqueResolved: rawRef && rawRef.match(/^[a-f0-9]{40}$/) && resolved,
- _integrity: false,
- _shasum: false
- }
- }
- })
- }
- function resolve (url, spec, name, opts) {
- const isSemver = !!spec.gitRange
- return git.revs(url, opts).then(remoteRefs => {
- return isSemver
- ? pickManifest({
- versions: remoteRefs.versions,
- 'dist-tags': remoteRefs['dist-tags'],
- name: name
- }, spec.gitRange, opts)
- : remoteRefs
- ? BB.resolve(
- remoteRefs.refs[spec.gitCommittish] || remoteRefs.refs[remoteRefs.shas[spec.gitCommittish]]
- )
- : null
- })
- }
- function withTmp (opts, cb) {
- if (opts.cache) {
- // cacache has a special facility for working in a tmp dir
- return cacache.tmp.withTmp(opts.cache, { tmpPrefix: 'git-clone' }, cb)
- } else {
- const tmpDir = path.join(osenv.tmpdir(), 'pacote-git-tmp')
- const tmpName = uniqueFilename(tmpDir, 'git-clone')
- const tmp = mkdirp(tmpName).then(() => tmpName).disposer(rimraf)
- return BB.using(tmp, cb)
- }
- }
- // Only certain whitelisted hosted gits support shadow cloning
- const SHALLOW_HOSTS = new Set(['github', 'gist', 'gitlab', 'bitbucket'])
- function cloneRepo (spec, repo, resolvedRef, rawRef, tmp, opts) {
- const ref = resolvedRef ? resolvedRef.ref : rawRef
- if (resolvedRef && spec.hosted && SHALLOW_HOSTS.has(spec.hosted.type)) {
- return git.shallow(repo, ref, tmp, opts)
- } else {
- return git.clone(repo, ref, tmp, opts)
- }
- }
|