GitLabyarnnpmnpm 淘宝源sha512源镜像

我终于找到 CI fail 的原因了!

就像我在这个 issue里说的那样,根本原因是

  1. 使用了 yarn offline cache
  2. 最近版本的 yarn 要校验 offline cache 的完整性
  3. 使用了淘宝 npm 源,但是淘宝 npm 源没有提供「完整性」这一字段

为了确证 3,我们可以尝试作请求

curl --silent https://registry.npm.taobao.org/serve-static/ | jq '.versions."1.13.2".dist'

返回

{
  "shasum": "095e8472fd5b46237db50ce486a43f4b86c6cec1",
  "size": 8400,
  "noattachment": false,
  "tarball": "http://registry.npm.taobao.org/serve-static/download/serve-static-1.13.2.tgz"
}

而支持 integrity 的源会多一个字段

curl --silent https://registry.npmjs.org/serve-static/ | jq '.versions."1.13.2".dist'

返回

{
  "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
  "shasum": "095e8472fd5b46237db50ce486a43f4b86c6cec1",
  "tarball": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
  "fileCount": 5,
  "unpackedSize": 24364
}

解决方法有

  1. 不使用淘宝源,并重新生成 yarn.lock 文件
  2. 提示淘宝源的维护人员加入这一字段

Colliot12/1/2018, 2:28:36 PM

它之前给我的错误提示是:

$ yarn
yarn install v1.12.3
[1/4] Resolving packages...
[2/4] Fetching packages...
error http://registry.npm.taobao.org/serve-static/download/serve-static-1.13.2.tgz: Integrity check failed for "serve-static" (computed integrity doesn't match our records, got "sha1-9axNemQgqZpqRa9HGfTc2M2Qekg=")
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
Colliot12/1/2018, 2:30:25 PM

这次终于彻彻底底搞明白原因了,我之前说的可能并不正确。当然现在这个新的理论似乎也无法解释前面 serve-static 包失败的原因——也许那还有待调查……

这次我发现一些 @types scope 下的包频繁出现安装失败,以及另一种更有意思的现象——被装成了不带 @types 的那个包,比如 @types/prop-types 被装成了 prop-types。这会导致 TypeScript 编译的时候频繁报告一些奇怪的错误,比如 TypeScript error: Cannot find type definition file for 'prop-types'. TS2688TypeScript error: Cannot find type definition file for 'retry'. TS2688,以及在净土本身的项目里似乎遇到过 TypeScript error: Cannot find type definition file for 'express'. TS2688,这个最后我发现也是因为@types/express 被装成了 `express。

前几天我忍无可忍,去调试了 yarn 的源码,发现 yarn 第二步去 fetch 包的步骤是这样的:

  1. 试图在 yarn 的缓存目录(macOS 下是 ~/Library/Caches/Yarn/v4/)里寻找缓存,比如 @types/prop-types 的缓存可能叫 `~/Library/Caches/Yarn/v4/npm-@types-prop-types-15.7.2-0e58ae66773d7fd7c372a493aff740878ec9ceaa/`。
  2. 如果这个缓存存在,那就使用这个缓存。如果启用了 yarn-offline-mirror,那么就把这个缓存移动到 offline mirror 里。(实际上这个缓存目录里的 node_modules/@types/prop-types 文件夹里除了存在包展开后的内容,还有一个压缩包 .yarn-tarball.tgz,它是直接拷贝过去的。
  3. 如果没有缓存,那就试图走离线缓存,即检查指定的 yarn-offline-mirror 目录里是否存在对应的压缩包。如果有,就用它。
  4. 如果啥都没有,那只能从网络上获取了,就是走指定的 npm/yarn 源下载咯。

在调试过程中,我还发现一些奇怪的现象,最终通过这些现象彻底搞清楚了问题在哪里(其实修法很简单,但真的是画一条线只值一美元,知道在哪里值 999999 美元……)。这些奇怪的现象有:

  1. 如果不用淘宝 npm 源,而用 npm 官方源或者 yarn 官方源,并把 yarn 缓存、yarn.lcok 和离线缓存清掉重装,就没有问题。
  2. yarn.lock 里的 integrity 字段竟然是不带 @types 那个包的
  3. yarn.lock 里带 @types 那个包的内容删掉(或者干脆整个文件都删掉)再装,不会出现 Integrity check failed,但是装上去的包还是错的
  4. yarn 缓存里的包就是错的…… ~/Library/Caches/Yarn/v4/npm-@types-prop-types-15.7.2-0e58ae66773d7fd7c372a493aff740878ec9ceaa/ 里的包竟然是 prop-types-15.7.2,而不是 `@types-prop-types-15.7.2`
  5. 删掉 yarn 缓存里的这条缓存之后再装,这个缓存竟然又变成了 prop-types……
  6. 把发网络请求那一段打出来,发现发的网络请求是对的。保存了网络请求的结果,发现结果也是对的(shasum 结果跟正确的压缩包相同)。那么 yarn 到底是从哪里搞到这个错误的包的内容的呢?yarn 试图装 prop-types 的时候,是从哪里搞到这个 prop-types 的内容的呢?

好了,高潮就要来了,咱们先歇会儿……

Colliot9/9/2019, 3:45:38 PM

那么它究竟是从哪里搞到这个 prop-types 的内容的呢?通过反复执行和调试,我终于发现,它在安装 @types/prop-types 时,竟然是在步骤 3 而不是步骤 4 出错的……也就是说,它的 Integrity check failed 的来源,是步骤 3 那个离线缓存文件……但是我去翻了那个目录,发现并没有这样的文件,这又是为什么呢?

这时候我看了那个文件的名字,终于发现了最后的答案——那个它试图读取的文件的名字,是 prop-types-15.7.2.tgz!!!!!

这个文件为什么会存在呢?是因为步骤 2——当我们先安装 prop-types 时,这个离线缓存会被先创造出来。当我们安装失败后,它又被回滚掉了,所以我们并看不见它。

现在的问题就来到了,为什么 yarn 在安装 @types/prop-types 时,却试图去读取 prop-types-15.7.2.tgz 呢?这已经非常容易了,我们只需要去看代码就行了。这一段代码在这里

    const match = pathname.match(RE_URL_NAME_MATCH);

    let packageFilename;
    if (match) {
      const [, scope, tarballBasename] = match;
      packageFilename = scope ? ``_mathjax_internal${scope}-$]^$`{tarballBasename}` : tarballBasename;
    } else {
      // fallback to base name
      packageFilename = path.basename(pathname);
    }

我们发现如果正则匹配失败,它就会取包的 basename,也就是没有 scope 的 name。那么为啥它会匹配失败呢?我们来看看这个正则

const RE_URL_NAME_MATCH = /\/(?:(@[^/]+)(?:\/|%2f))?[^/]+\/(?:-|_attachments)\/(?:@[^/]+\/)?([^/]+)$/;

看起来非常复杂,但我们只要关心这个中缀 \/(?:-|_attachments)\/ 就行了。它是啥呢?npm 源会先返回一个包的 meta info,比如 @types/prop-types 的:https://registry.npmjs.org/@types/prop-types ,里面有每个版本的下载地址,比如 15.7.2 的就是 https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.2.tgz。这个中缀正则就是为了匹配 URL 里的 /-/ 这段。问题就出在这里——淘宝源 https://registry.npm.taobao.org/@types/prop-types 返回的 URL 是 https://registry.npm.taobao.org/@types/prop-types/download/@types/prop-types-15.7.2.tgz,中缀是 /download/,这显然并无法被匹配,所以就走到了 fallback 的分支,去读取 basename 也就是 prop-types 对应的缓存了……

于是真相大白了。解决方案也很简单,只要给这个中缀正则加上 download 的情形即可。提了一个 pull request,前途未卜。

Colliot9/9/2019, 4:10:00 PM

现在还有两个疑点没解决:

  1. 没有 integrity 字段到底碍不碍事?
  2. 为啥读取 yarn 离线缓存报错之后停止安装了,但还是会发网络请求?
Colliot9/10/2019, 11:23:26 AM

关于帖子主干中提到的问题,为什么 serve-static 并不是一个 @types 下的包,却依然发生了错误?

这是因为它有一个 @types/serve-static 的对应包,并且 yarn.lockserve-staticintegrity 被写成了 @types/serve-static 的,这样你怎么装都装不对了。

Colliot9/11/2019, 3:28:55 AM

所以,如何避免这个问题?只能替换淘宝源为官方源吗?

someone3/12/2020, 2:46:53 AM

所以,如何避免这个问题?只能替换淘宝源为官方源吗?

——someone

可以使用我修改过的这个分支 Fixes offline mirror filename calculation for scoped packages URLs in the taobao npm registry #7533

Colliot3/12/2020, 5:12:28 AM

预览:

取消

其他地方

nickname 回复了 Git 如何 pull --force?

-f force When git fetch is used with <src>:<dst> refspec it may refuse to update the local branch as discussed in the <refspec> part of the git-fetch[1] documentation. This option overrides that check. https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---force

Colliot 回复了 `An unexpected error occurred: "Cannot create property '-npm-taobao-org-mirrors-node-sass-' on string '{\"-npm-taobao-org-mirrors-node-sass-\":true}'".` 会是什么原因?

error An unexpected error occurred: "Cannot create property '-npm-taobao-org-mirrors-node-sass-' on string '{"-npm-taobao-org-mirrors-node-sass-":true}'". 又出现了这个问题

caijin 回复了 前端开发如何解决画 UI 的问题?

UI 要你吃xx,你能不吃吗

ginv0_ 回复了 计算机组成应该怎么学?

自学。唐朔飞的;(本校)和体系结构一起学的;实验不多(学长有火炬可以抄..);要写一些verilog,南大的实验推荐,GitHub上有代码。

crazy95sun 回复了 为什么不能对 C++ 的语法进行简化?

如果允许 void <T>addName(T&& name) { names_.push_back(forward(name)); 就应该允许 void <T>addName(T&& /unnamed/) { 而形如A < B > C (D, E)的代码可能产生很大的歧义

Colliot 回复了 随机过程应该怎么学?

一个有限集合的子集,就是从原集合中选取一些元素构成的。对于每一个元素,无非是选或不选两种可能。对于 n 个元素,就是 2^n 种可能,每种可能对应一个子集,所以一共有 2^n 种。

Colliot 回复了 随机过程应该怎么学?

A subset B 真的成立吗?为什么会觉得成立呢?A 的元素 1 也在 B 中吗?

Colliot 回复了 随机过程应该怎么学?

我感觉需要一点基本常识,比如 对无限集合的感觉 对不可数无限集合的感觉 当我们在谈论一个被定义出来的对象的时候,我们并不一定是要完全描绘出这个对象。我们可能只是谈论关于这个对象的某些命题,比如当我们在谈论集合 A 的时候: 我们可能只想知道,对于给定的 x,是否有 x in A——也许我们并不想,也不能枚举出 A d 所有元素。 我们可能想知道,对于给定的 x in A, y in A,对 x, y 进行某种操作之后得到的结果 z 是否还属于 A。 对满足某一性质的「最小」集合的存在性的感知。理论上这依赖 Zorn 引理,但直觉上这并不是很难接受的。 只有熟稔了这些常识,才可以知道 sigma-代数是什么,或者,更本质地,就知道了我们为什么要定义 sigma-代数、 sigma-代数的定义为什么会是这样的。 尽管现代数学的定义粗看起来艰涩而反直觉,但它背后其实有着跟其他易懂的概念一样精妙的直觉——我们上面提到的这些常识,就是这些「直觉」和艰涩的「公理化定义」互相转换的关键。

Gagaxi 回复了 随机过程应该怎么学?

1.为为什么?qaq .P(D)={∅,{1},{2},{1,2}} .2^5 个