虽然在CI中引入ESLint能极大地帮助代码的规范,但是它也会带来额外的成本。
其中最关键的,就是CI执行过程中的时间消耗了。
一个一般规模的前端工程,执行npm install
操作需要几分钟,执行eslint
又会需要一两分钟。因此在CI中引入ESLint大致会将CI流程的时间延长好几分钟。如果项目比较大,问题可能会更严重。
正是因为这个原因,很多团队不得不将eslint从流水线中删除。
然而这个问题真的无解吗?其实不然。
从上面的分析中能看出,有两处主要的性能优化点。一个是npm install
过程,另一个是eslint
过程,下面我分别给出它们各自的优化方案。
大部分时候,npm 依赖的内容是不变的。所以反反复复地安装它,就有些浪费。我们可以通过 Docker cache 和依赖前置来解决这个问题。
假设优化之前的Dockerfile如下
FROM node:alpine-12
WORKDIR /code
ADD . /code
RUN npm install
RUN npx eslint .
因为 ADD . /code
这个步骤,每次代码变更时都会重新执行,所以后面的npm install
自然就无可用的cache,即每次都需要重新安装。
如果稍微调整一下,将依赖文件前置:
FROM node:alpine-12
WORKDIR /code
COPY package.json package-lock.json /code # 提前复制依赖文件
RUN npm install
ADD . /code
RUN npx eslint .
这样一来,只要 package.json 和 package-lock.json文件的内容没有发生变化,npm install
的步骤就可以命中cache直接跳过。
因为在大部分时候,package.json和package-lock.json文件的内容不变,npm install
能直接跳过,所以能节省下来几分钟的时间。
ESLint一般只会花费1~2分钟,但是蚊子再小也是肉,还是有优化空间的。
首先,因为CI中一般只需要检测 eslint error,无需处理 warning。所以可以直接打开 --quiet
选项忽略warnings。如果当前代码质量不高,警告比较多的话,打开这个选项能节省不少的处理时间。
然后,还可以利用 eslint cache 来进一步压缩其运行时间。
ESLint运行时,支持设置 --cache 选项和 --cache-location 选项,分别用于启用 eslint cache 和设置eslint cache的存储位置。
为了能够利用 Docker cache,我们需要在多次流水线之间共享存储,Dockerfile需要做一些调整。
-
首先,因为 docker build 时还不支持挂载Volume,所以要将 eslint 具体执行操作移动到 CMD 环节中,并在之后运行它。即:
FROM node:alpine-12 WORKDIR /code COPY package.json package-lock.json /code # 提前复制依赖文件 RUN npm install ADD . /code CMD npx eslint .
$ docker build . -t my-eslint-image $ docker run my-eslint-image
-
然后,设置下共享Volume:
$ docker build . -t my-eslint-image $ docker run -v /tmp/cache:/cache my-eslint-image # 将host上的/tmp/cache映射到容器的 /cache目录
-
最后,打开 eslint 的 cache 和 cache-location 选项:
FROM node:alpine-12 WORKDIR /code COPY package.json package-lock.json /code # 提前复制依赖文件 RUN npm install ADD . /code CMD npx eslint . --cache --cache-location /cache/.eslintcache
这样就设置好了。每次运行eslint时,它会从 /cache/.eslintcache (对应于host的 /tmp/cache/.eslintcache )中读取cache信息,并在运行结束后更新这个文件。
在这个步骤完成后,每次eslint运行时,只需要处理有变动的文件。时间也会大大缩短。
以上就是我在CI中运行ESLint的经验,希望对你有所帮助!