仅当未缓存或 package.json 在 gitlab ci 中更改时,我如何 运行 依赖项安装作业?

How can I run dependency install job only when it's not cached or package.json changed in gitlab ci?

我在 gitlab 中有一个带有 angular 前端和 nestjs 后端的 monorepo。我对它们每个都有 package.json,在根目录中有 1 个。我的管道由多个阶段组成,如下所示:

stages:
  - build
  - verify
  - test
  - deploy

我在 .pre 阶段有一份安装依赖项的工作。我想缓存作业之间以及分支之间的那些,如果 package-lock.json 中的任何一个发生了变化,而且如果当前没有缓存 node_modules。 我有一份看起来像这样的工作:

prepare:
  stage: .pre
  script:
    - npm run ci-deps # runs npm ci in each folder
  cache:
    key: $CI_PROJECT_ID
    paths:
      - node_modules/
      - frontend/node_modules/
      - backend/node_modules/
    only:
      changes:
        - '**/package-lock.json'

现在的问题是,如果缓存以某种方式被清除,或者如果我没有在第一次推送时引入对 package-lock.json 的更改,我将根本没有这项工作 运行,因此一切否则会失败,因为它需要 node_modules。如果我从那里删除 changes:,那么它会为每个管道运行该作业。当然,我仍然可以在作业之间共享它,但是如果我再做一次提交并推送,安装所有依赖项需要将近 2 分钟,即使我没有改变任何应该存在的东西......我错过了什么吗?我怎样才能以某种方式缓存它,以便它只在缓存过时或不存在时才重新安装依赖项?

最后我认为我可以在不依赖 gitlab ci 功能的情况下做到这一点,但是我自己做检查:

prepare:
  stage: .pre
  image: node:12
  script:
    - if [[ ! -d node_modules ]] || [[ -n `git diff --name-only HEAD~1 HEAD | grep "\package.json\b"` ]];
      then
      npm ci;
      fi
    - if [[ ! -d frontend/node_modules ]] || [[ -n `git diff --name-only HEAD~1 HEAD | grep "\frontend/package.json\b"` ]];
      then
      npm run ci-deps:frontend;
      fi
    - if [[ ! -d backend/node_modules ]] || [[ -n `git diff --name-only HEAD~1 HEAD | grep "\backend/package.json\b"` ]];
      then
      npm run ci-deps:backend;
      fi
  cache:
    key: '$CI_COMMIT_REF_SLUG-$CI_PROJECT_DIR'
    paths:
      - node_modules/
      - frontend/node_modules
      - backend/node_modules

这样做的好处是它只会为项目的 specific 部分安装 dependencies,如果它还没有 node_modules 或者什么时候package.json 已更改。但是,如果我推送多个提交并且 package.json 不会在最后一个中更改,这可能是错误的。在那种情况下,我仍然可以手动清除缓存并重新运行管道,但我会尝试进一步改进我的脚本并更新我的答案。

我遇到了同样的问题,我可以使用关键字 rules 而不是 only|except 来解决它。使用它,您可以声明更复杂的情况,例如使用 ifexistschanges。另外,这个:

Rules can't be used in combination with only/except because it is a replacement for that functionality. If you attempt to do this, the linter returns a key may not be used with rules error.

-- https://docs.gitlab.com/ee/ci/yaml/#rules

更有理由切换到 rules。这是我的解决方案,它执行 npm ci :

  • 如果 package-lock.json 文件被修改

  • 或者如果 node-modules 文件夹不存在(在新分支或缓存清理的情况下):
npm-ci:
  image: node:lts
  cache:
    key: $CI_COMMIT_REF_SLUG-$CI_PROJECT_DIR
    paths:
      - node_modules/
  script:
    - npm ci
  rules:
    - changes:
        - package-lock.json
    - exists:
        - node_modules
      when: never

希望对您有所帮助!

规则:Exists 在缓存被拉下之前运行,所以这对我来说不是一个可行的解决方案。

在 GitLab v12.5 中我们现在可以使用 cache:key:files

如果我们将其与 Blind Despair 的部分条件逻辑相结合,我们将得到一个很好的解决方案

prepare:
  stage: .pre
  image: node:12
  script:
    - if [[ ! -d node_modules ]];
      then
        npm ci;
      fi
  cache:
    key:
      files:
        - package-lock.json
      prefix: nm-$CI_PROJECT_NAME
    paths:
      - node_modules/

然后我们可以在后续的构建作业中使用它

# let's keep it dry with templates
.use_cached_node_modules: &use_cached_node_modules
  cache:
    key:
      files:
        - package-lock.json
      prefix: nm-$CI_PROJECT_NAME
    paths:
      - node_modules/
    policy: pull # don't push unnecessarily

build:
  <<: *use_cached_node_modules
  stage: build
  image: node:12
  script:
    - npm run build

我们通过共享缓存在多个分支上成功地使用了它。