источник
Однажды при установке зависимостей проекта терминал сообщил о следующей ошибке, что привело к сбою установки зависимостей.
Из сообщения об ошибке видно, что@sentry/cli
Причина этого пакета в том, что проект напрямую не зависит от этого пакета.Чтобы исключить влияние между пакетами, была создана новая папка, и этот пакет был установлен отдельно, но обнаружилось, что сообщается об одной и той же ошибке. Затем я попросил своих коллег установить пакет и опробовать его, и обнаружил, что все нормально и никаких ошибок не было.
Далее идет комплексная операция: поиск в Google, поиск проблем на github, переход на установку npm, переключение источника npm, переключение версии узла, установка других версий.@sentry/cli
, очистить кэш пряжи и npm, перезагрузить компьютер. . . Однако оказалось, что это бесполезно. . .
Кажется, что все не так просто
Оглядываясь назад на сообщение об ошибке, вы можете обнаружить, что оно выполняетсяnode scripts/install.js
Если возникает ошибка, извлеките код и запустите его локально. просто сделай это, положи это@sentry/cli
После клонирования на локал сначала установите зависимости, а потом выполнитеnode scripts/install.js
Нашел следующую ошибку:
Discovery действительно работает/Users/sliwey/githome/sentry-cli/sentry-cli --version
Во время выполнения команды произошла ошибка.По указанному выше пути обнаружено, что в корневом каталоге проекта есть лишний файл с именем.sentry-cli
запускаемый файл.
Значит с этим файлом должна быть проблема, так откуда этот файл, посмотриscripts/install.js
код, вы обнаружите, что он на самом деле делает одну вещь:
downloadBinary()
.then(() => checkVersion())
.then(() => process.exit(0))
.catch(e => {
console.error(e.toString());
process.exit(1);
});
Просто скачайте исполняемый файл и проверьте номер версии.checkVersion
Сначала нажмите на стол, это не главное, просто оцените номер версии, посмотримdownloadBinary
(я упростил код и добавил комментарии, конкретный код можно посмотретьGitHub.com/gets entry/ — это…):
function downloadBinary() {
const arch = os.arch();
const platform = os.platform();
const outputPath = helper.getPath();
// 根据不同系统获取对应的下载链接
const downloadUrl = getDownloadUrl(platform, arch);
// 根据下载链接生成缓存路径
const cachedPath = getCachedPath(downloadUrl);
// 缓存命中,就把文件复制到当前路径下
if (fs.existsSync(cachedPath)) {
copyFileSync(cachedPath, outputPath);
return Promise.resolve();
}
// 缓存未命中,就下载,并把文件写入缓存
return fetch(downloadUrl, { redirect: 'follow', agent }).then(response => {
const tempPath = getTempFile(cachedPath);
mkdirp.sync(path.dirname(tempPath));
return new Promise((resolve, reject) => {
response.body
.pipe(fs.createWriteStream(tempPath, { mode: '0755' }))
}).then(() => {
copyFileSync(tempPath, cachedPath);
copyFileSync(tempPath, outputPath);
fs.unlinkSync(tempPath);
});
});
}
По ситуации с локальным выполнением сейчас загрузки нет, и видно, что исполняемый файл берется из кеша, затем делаем брейкпоинт и смотрим путь к кешу:
По полученному пути удалите соответствующий файл, а потом переустановите, все ок~
Суть в следующем
Хоть проблема и решена, вспомнил про предыдущую однопроходную операцию.Среди них была очистка кеша, в том числе пряжи и npm.Метод на тот момент делался через следующие две команды:
yarn cache clean
npm cache clean --force
По полученному выше пути кэша можно узнатьsentry-cli
кэшируется в~/.npm
Под папку, так что с пряжей должно быть все в порядке, и ее надо исключить в первую очередь. Затем посмотрите на npm и найдите это черезnpm cache clean --force
очистить кеш и не очистить~/.npm
Файлы в папке, то где эта команда понятно? Посмотрим, что написано в документации:npm-cache
Для удобства чтения сделал несколько фото:
На первый взгляд кажется, что все в порядке, я проверил конфигурацию своего кеша и не нашел ничего необычного:
Так в чем проблема?Похоже,мы можем только посмотреть исходный код.Цель очень прямая.Найти код связанный с кешем в npm,а потом уже непосредственно смотреть на реализацию метода clean(для конкретного кода , видетьlib/cache.js): function clean (args) {
if (!args) args = []
if (args.length) {
return BB.reject(new Error('npm cache clear does not accept arguments'))
}
// 重点在这
// npm.cache就是 ~/.npm
// 所以cachePath的值应该是 ~/.npm/_cacache
const cachePath = path.join(npm.cache, '_cacache')
if (!npm.config.get('force')) {
return BB.reject(new Error("As of npm@5, the npm cache self-heals from corruption issues and data extracted from the cache is guaranteed to be valid. If you want to make sure everything is consistent, use 'npm cache verify' instead. On the other hand, if you're debugging an issue with the installer, you can use `npm install --cache /tmp/empty-cache` to use a temporary cache instead of nuking the actual one.\n\nIf you're sure you want to delete the entire cache, rerun this command with --force."))
}
// TODO - remove specific packages or package versions
return rm(cachePath)
}
Ясно видеть это,npm cache clean --force
ясно~/.npm/_cacache
данные в папке.
Если подумать, этот момент не должен упоминаться в документе. Вернитесь и снова просмотрите документ и обнаружите, что я пропустил часть содержания. . .
Содержание следующее:
Просто введитеnpm@5
После этого npm помещает кэшированные данные в конфигурационный файл.cache
Ниже путь конфигурации поля_cacache
папка. Объединяя содержание двух вышеприведенных абзацев документов, можно сделать вывод, что:
- в файле конфигурации
cache
Конфигурация поля - это корневой каталог - Кэшированные данные помещаются в корневой каталог
_cacache
в папке -
clean
Команда очистки_cacache
папка
Что именно хранится в кеше npm
Открыть_cacache
папку и обнаружил, что она не похожаnode_modules
Внутри такой же упаковки по одному, но вот так:
открыть для себяcontent-v2
В нем в основном какие-то бинарные файлы, и расширение бинарного файла изменено на.tgz
После распаковки вы обнаружите, что он находится в знакомом нам пакете npm.index-v5
Внутри также есть некоторые описательные документы.content-v2
Индекс файла в файле, если вы внимательно посмотрите, вы обнаружите, что он немного похож на заголовок ответа HTTP, и есть значения, связанные с кешем:
Так как же генерируются эти файлы? Из приведенного выше документа мы можем узнать, что npm в основном используетpacoteЧтобы установить пакет, давайте посмотрим, как npm использует pacote в коде. Есть три основных места, где npm будет использовать pacote:
- npm установить xxx (через
pacote.extract
Соответствующий пакет в соответствующей декомпрессииnode_modules
под. Запись исходного кода npm:lib/install/action/extract-worker.js, запись исходного кода пакета:extract.js) - npm cache add xxx (через
pacote.tarball.stream
прошлое~/.npm/_cacache
Добавьте кешированные данные. Запись исходного кода npm:lib/cache.js, запись исходного кода пакета:tarball.js#tarballStream) - npm pack xxx (через
pacote.tarball.toFile
Создайте соответствующий сжатый файл по текущему пути. Запись исходного кода npm:lib/pack.js, запись исходного кода пакета:tarball.js#tarballToFile)
Сравнивая три вышеупомянутых метода пакота, можно обнаружить, что основными зависимыми методами являютсяlib/withTarballStream.js, кода больше, упростите его, в основном посмотрите на китайские комментарии:
function withTarballStream (spec, opts, streamHandler) {
opts = optCheck(opts)
spec = npa(spec, opts.where)
// 读本地文件
const tryFile = (
!opts.preferOnline &&
opts.integrity &&
opts.resolved &&
opts.resolved.startsWith('file:')
)
? BB.try(() => {
const file = path.resolve(opts.where || '.', opts.resolved.substr(5))
return statAsync(file)
.then(() => {
const verifier = ssri.integrityStream({ integrity: opts.integrity })
const stream = fs.createReadStream(file)
.on('error', err => verifier.emit('error', err))
.pipe(verifier)
return streamHandler(stream)
})
: BB.reject(Object.assign(new Error('no file!'), { code: 'ENOENT' }))
// 上一步reject之后,从缓存中读
const tryDigest = tryFile
.catch(err => {
if (
opts.preferOnline ||
!opts.cache ||
!opts.integrity ||
!RETRIABLE_ERRORS.has(err.code)
) {
throw err
} else {
// 通过cacache来读缓存中的数据
const stream = cacache.get.stream.byDigest(
opts.cache, opts.integrity, opts
)
stream.once('error', err => stream.on('newListener', (ev, l) => {
if (ev === 'error') { l(err) }
}))
return streamHandler(stream)
.catch(err => {
if (err.code === 'EINTEGRITY' || err.code === 'Z_DATA_ERROR') {
opts.log.warn('tarball', `cached data for ${spec} (${opts.integrity}) seems to be corrupted. Refreshing cache.`)
// 当错误码为EINTEGRITY或Z_DATA_ERROR时,清除缓存
return cleanUpCached(opts.cache, opts.integrity, opts)
.then(() => { throw err })
} else {
throw err
}
})
}
})
// 上一步reject之后,再下载
const trySpec = tryDigest
.catch(err => {
if (!RETRIABLE_ERRORS.has(err.code)) {
// If it's not one of our retriable errors, bail out and give up.
throw err
} else {
return BB.resolve(retry((tryAgain, attemptNum) => {
// 下载包,这边其实是通过npm-registry-fetch来下载的
const tardata = fetch.tarball(spec, opts)
if (!opts.resolved) {
tardata.on('manifest', m => {
opts = opts.concat({ resolved: m._resolved })
})
tardata.on('integrity', i => {
opts = opts.concat({ integrity: i })
})
}
return BB.try(() => streamHandler(tardata))
}, { retries: 1 }))
}
})
return trySpec
.catch(err => {
if (err.code === 'EINTEGRITY') {
err.message = `Verification failed while extracting ${spec}:\n${err.message}`
}
throw err
})
}
Из приведенного выше кода вы можете узнать, что pacote использует npm-registry-fetch для загрузки пакетов. Проверьте документацию npm-registry-fetch и найдитеcache
Свойства могут быть установлены:npm-registry-fetch#opts.cache
Видно, что если установитьcache
Значение (в npm равно~/.npm/_cacache
), он создаст по заданному пути согласноIETF RFC 7234Сгенерированные данные кэша. Откройте адрес rfc и обнаружите, что это документ, описывающий HTTP-кеш, так что то, что сказано в начале этого абзацаindex-v5
Следующий файл также легко понять.
Кратко обобщить:
-
~/.npm/_cacache
Некоторые хранятся в двоичном файле и имеют соответствующий индекс. - При установке npm, если есть кеш, соответствующий бинарный файл будет распакован в соответствующий бинарный файл через pacote
node_modules
под. - Сам npm предоставляет методы только для очистки кеша и проверки целостности кеша, а не для прямого управления кешем.
cacache
для управления кэшированными данными.
напиши в конце
Оглядываясь на все это, я понял, как важно внимательно смотреть документацию! Запомнить! Запомнить! Но я также разобрал моменты, на которые в обычное время не обращаю особого внимания, что можно расценивать как выигрыш.Записывается в виде текста для удобства просмотра.
Оригинальная ссылка:GitHub.com/Gift Malicious/блог…