Github Action npm缓存最简示例失效问题排查与修复
问题根源分析
npm缓存认知偏差
你混淆了~/.npm(npm下载缓存目录)与项目node_modules目录的作用:~/.npm仅存储npm下载的包压缩包,用于避免重复从网络拉取,但不会自动生成项目的node_modules目录。因此即使恢复了~/.npm缓存,项目目录下依然没有已安装的依赖,npm list报缺失是正常现象——但此时执行npm install应该会跳过下载步骤,直接从缓存解压到node_modules,速度会显著提升。缓存未被有效保存
从deploy任务的缓存恢复输出可见,缓存大小仅195B,说明build任务中保存的~/.npm目录几乎是空的,没有实际的包缓存。原因包括:- GitHub Actions runner中npm的默认缓存路径可能不是
~/.npm,硬编码路径会导致缓存抓空; - 缓存保存条件
steps.cache-npm-restore.outputs.cache-hit != 'true'存在逻辑缺陷:若后续build任务命中旧缓存,即使依赖已更新,也不会保存新缓存; - deploy任务缺失
setup-node步骤,可能导致Node/npm版本与build任务不一致,缓存无法复用。
- GitHub Actions runner中npm的默认缓存路径可能不是
修复方案
推荐两种方案,按需选择:
方案一:直接缓存项目node_modules目录(最直观,直接复用已安装依赖)
这种方式直接缓存项目内的node_modules,恢复后无需重复执行完整的npm install,适合需要快速恢复项目依赖的场景。
修改后的完整工作流:
name: Build on: push: branches: - main - fix-* - feature-* workflow_dispatch: env: nodejs_version: ${{ github.event.inputs.nodejs_version || '20.4.0' }} repo_dir: repo cache_name: npm-node-modules-cache jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Clone Deploy Repository (Latest) uses: actions/checkout@v3 with: path: ${{ env.repo_dir }} - name: Setup NodeJS uses: actions/setup-node@v3 with: node-version: ${{ env.nodejs_version }} - name: Restore node_modules cache uses: actions/cache/restore@v3 id: cache-node-modules-restore with: path: ${{ env.repo_dir }}/node_modules key: ${{ runner.os }}-node-${{ env.nodejs_version }}-${{ hashFiles(format('{0}/package-lock.json', env.repo_dir)) }} restore-keys: | ${{ runner.os }}-node-${{ env.nodejs_version }}- - name: Install NodeJS modules (cache miss only) if: ${{ steps.cache-node-modules-restore.outputs.cache-hit != 'true' }} run: | cd $repo_dir && npm install - name: Save node_modules cache if: ${{ steps.cache-node-modules-restore.outputs.cache-hit != 'true' }} uses: actions/cache/save@v3 with: path: ${{ env.repo_dir }}/node_modules key: ${{ steps.cache-node-modules-restore.outputs.cache-primary-key }} - name: Build run: | cd $repo_dir && npm run build - name: Test run: | cd $repo_dir && npm run test deploy: name: Deploy runs-on: ubuntu-latest needs: build steps: - name: Clone Deploy Repository (Latest) uses: actions/checkout@v3 with: path: ${{ env.repo_dir }} - name: Setup NodeJS uses: actions/setup-node@v3 with: node-version: ${{ env.nodejs_version }} - name: Restore node_modules cache uses: actions/cache/restore@v3 id: cache-node-modules-restore with: path: ${{ env.repo_dir }}/node_modules key: ${{ runner.os }}-node-${{ env.nodejs_version }}-${{ hashFiles(format('{0}/package-lock.json', env.repo_dir)) }} restore-keys: | ${{ runner.os }}-node-${{ env.nodejs_version }}- - name: Install NodeJS modules (cache miss only) if: ${{ steps.cache-node-modules-restore.outputs.cache-hit != 'true' }} run: | cd $repo_dir && npm install - name: Deploy run: | cd $repo_dir && npm run deploy
修改要点:
- 缓存路径改为项目的
node_modules目录; - 缓存key加入
package-lock.json哈希值,确保依赖变更时缓存自动更新; - 加入
restore-keys,允许匹配同Node版本的旧缓存,提升命中率; - deploy任务补充
setup-node步骤,保证版本一致性。
方案二:正确缓存npm下载缓存(加速npm install的下载阶段)
若想通过缓存npm下载包来加速安装,需确保缓存路径正确,并加入依赖哈希避免缓存过时:
关键步骤修改:
# 在build和deploy任务中都需要执行以下步骤 - name: Setup NodeJS uses: actions/setup-node@v3 with: node-version: ${{ env.nodejs_version }} - name: Get npm cache directory id: npm-cache-dir run: | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - name: Restore npm cache uses: actions/cache/restore@v3 id: cache-npm-restore with: path: ${{ steps.npm-cache-dir.outputs.dir }} key: ${{ runner.os }}-npm-${{ hashFiles(format('{0}/package-lock.json', env.repo_dir)) }} restore-keys: | ${{ runner.os }}-npm- - name: Install NodeJS modules run: | cd $repo_dir && npm install --prefer-offline - name: Save npm cache if: ${{ steps.cache-npm-restore.outputs.cache-hit != 'true' }} uses: actions/cache/save@v3 with: path: ${{ steps.npm-cache-dir.outputs.dir }} key: ${{ steps.cache-npm-restore.outputs.cache-primary-key }}
修改要点:
- 通过
npm config get cache动态获取实际缓存路径,避免硬编码错误; - 缓存key加入
package-lock.json哈希,确保依赖更新时缓存同步更新; - npm install添加
--prefer-offline参数,强制优先使用本地缓存; - deploy任务必须补充
setup-node步骤,保证版本与build任务一致。
额外说明
- 若使用方案二,恢复缓存后
npm list依然会报依赖缺失(因为node_modules未生成),但npm install会跳过下载步骤,直接从缓存解压,速度会明显加快; - 两种方案都必须将Node版本、依赖哈希加入缓存key,否则会出现缓存失效或依赖不兼容问题。
内容的提问来源于stack exchange,提问作者vy218
相关产品推荐
相关产品推荐

