logo

鱼肚的博客

Don't Repeat Yourself

CI中运行ESLint的痛点

虽然在CI中引入ESLint能极大地帮助代码的规范,但是它也会带来额外的成本。

其中最关键的,就是CI执行过程中的时间消耗了。

一个一般规模的前端工程,执行npm install操作需要几分钟,执行eslint又会需要一两分钟。因此在CI中引入ESLint大致会将CI流程的时间延长好几分钟。如果项目比较大,问题可能会更严重。

正是因为这个原因,很多团队不得不将eslint从流水线中删除。

然而这个问题真的无解吗?其实不然。

优化方案

从上面的分析中能看出,有两处主要的性能优化点。一个是npm install过程,另一个是eslint过程,下面我分别给出它们各自的优化方案。

优化 npm install 过程

大部分时候,npm 依赖的内容是不变的。所以反反复复地安装它,就有些浪费。我们可以通过 Docker cache 和依赖前置来解决这个问题。

假设优化之前的Dockerfile如下

1FROM node:alpine-12
2WORKDIR /code
3ADD . /code
4RUN npm install
5RUN npx eslint .

因为 ADD . /code这个步骤,每次代码变更时都会重新执行,所以后面的npm install自然就无可用的cache,即每次都需要重新安装。

如果稍微调整一下,将依赖文件前置:

1FROM node:alpine-12
2WORKDIR /code
3COPY package.json package-lock.json /code # 提前复制依赖文件
4RUN npm install
5
6ADD . /code
7RUN npx eslint .

这样一来,只要 package.json 和 package-lock.json文件的内容没有发生变化,npm install的步骤就可以命中cache直接跳过。

因为在大部分时候,package.json和package-lock.json文件的内容不变,npm install能直接跳过,所以能节省下来几分钟的时间。

优化 eslint 过程

ESLint一般只会花费1~2分钟,但是蚊子再小也是肉,还是有优化空间的。

关闭警告

首先,因为CI中一般只需要检测 eslint error,无需处理 warning。所以可以直接打开 --quiet选项忽略warnings。如果当前代码质量不高,警告比较多的话,打开这个选项能节省不少的处理时间。

然后,还可以利用 eslint cache 来进一步压缩其运行时间。

打开Cache

ESLint运行时,支持设置 --cache 选项和 --cache-location 选项,分别用于启用 eslint cache 和设置eslint cache的存储位置。

为了能够利用 Docker cache,我们需要在多次流水线之间共享存储,Dockerfile需要做一些调整。

  1. 首先,因为 docker build 时还不支持挂载Volume,所以要将 eslint 具体执行操作移动到 CMD 环节中,并在之后运行它。即:

    1FROM node:alpine-12
    2WORKDIR /code
    3COPY package.json package-lock.json /code # 提前复制依赖文件
    4RUN npm install
    5
    6ADD . /code
    7CMD npx eslint .
    1$ docker build . -t my-eslint-image
    2$ docker run my-eslint-image
    
  2. 然后,设置下共享Volume:

    1$ docker build . -t my-eslint-image
    2$ docker run -v /tmp/cache:/cache my-eslint-image # 将host上的/tmp/cache映射到容器的 /cache目录
  3. 最后,打开 eslint 的 cache 和 cache-location 选项:

    1FROM node:alpine-12
    2WORKDIR /code
    3COPY package.json package-lock.json /code # 提前复制依赖文件
    4RUN npm install
    5
    6ADD . /code
    7CMD npx eslint . --cache --cache-location /cache/.eslintcache

这样就设置好了。每次运行eslint时,它会从 /cache/.eslintcache (对应于host的 /tmp/cache/.eslintcache )中读取cache信息,并在运行结束后更新这个文件。

在这个步骤完成后,每次eslint运行时,只需要处理有变动的文件。时间也会大大缩短。

以上就是我在CI中运行ESLint的经验,希望对你有所帮助!