Files
xgj/npm-install/action.yml

284 lines
9.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: 'npm依赖安装与缓存'
description: '自动缓存和安装npm依赖支持多种包管理器'
branding:
icon: 'package'
color: 'blue'
inputs:
package-manager:
description: '包管理器类型 (npm, pnpm, yarn)'
required: false
default: 'pnpm'
pnpm-version:
description: 'pnpm 版本(当 package-manager=pnpm 时生效)'
required: false
default: '10'
cache-mode:
description: '缓存模式node_modules 或 store'
required: false
default: 'store'
optimize-install-flags:
description: '是否启用安装参数优化pnpm+store时自动使用 --offline/--prefer-offline 与 --frozen-lockfile(true/false)'
required: false
default: 'true'
cache-prefix:
description: '缓存前缀名称'
required: false
default: 'modules'
node-modules-path:
description: 'node_modules目录路径'
required: false
default: 'node_modules'
force-install:
description: '是否强制安装 (true/false)'
required: false
default: 'false'
enable-git-stash:
description: '安装后是否执行git stash (true/false)'
required: false
default: 'false'
install-command:
description: '自定义安装命令(可选,会覆盖默认命令)'
required: false
default: ''
install-args:
description: '附加到默认安装命令的参数(当未提供 install-command 时生效)'
required: false
default: ''
cache-hash:
description: '缓存hash值推荐使用hashFiles计算'
required: false
default: ""
outputs:
cache-hit:
description: '是否命中缓存 (true/false)'
value: ${{ steps.cache.outputs.cache-hit }}
cache-key:
description: '使用的缓存key'
value: ${{ steps.cache-key.outputs.key }}
cache-path:
description: '缓存路径(用于调试与复用)'
value: ${{ steps.cache-path.outputs.path }}
runs:
using: 'composite'
steps:
- name: 生成缓存key
id: cache-key
shell: bash
env:
FALLBACK_HASH: ${{ hashFiles('package.json') }}
run: |
# 确定使用的hash值
if [[ -n "${{ inputs.cache-hash }}" && "${{ inputs.cache-hash }}" != "" ]]; then
CACHE_HASH="${{ inputs.cache-hash }}"
echo "✅ 使用用户传入的hash值"
elif [[ -n "${FALLBACK_HASH}" && "${FALLBACK_HASH}" != "" ]]; then
CACHE_HASH="${FALLBACK_HASH}"
echo "📝 未提供hash使用package.json作为fallback"
else
CACHE_HASH=""
echo "⚠️ 警告: 无法获取hash值使用默认值"
fi
# 截取hash的前12位
if [[ -n "${CACHE_HASH}" && "${CACHE_HASH}" != "" ]]; then
CACHE_HASH_SHORT=$(echo "${CACHE_HASH}" | head -c 12)
echo "✅ 成功计算缓存hash: ${CACHE_HASH_SHORT}"
else
CACHE_HASH_SHORT="no-hash"
echo "⚠️ 使用默认hash值: ${CACHE_HASH_SHORT}"
fi
# 生成包管理器后缀,避免不同包管理器/版本的缓存互相污染
MANAGER="${{ inputs.package-manager }}"
MANAGER_SUFFIX="$MANAGER"
if [[ "$MANAGER" == "pnpm" && -n "${{ inputs.pnpm-version }}" && "${{ inputs.pnpm-version }}" != "" ]]; then
MANAGER_SUFFIX="${MANAGER}-v${{ inputs.pnpm-version }}"
fi
# 模式后缀隔离不同缓存模式node_modules vs store
MODE_SUFFIX="${{ inputs.cache-mode }}"
if [[ -z "$MODE_SUFFIX" ]]; then MODE_SUFFIX="node_modules"; fi
# 构建缓存key<OS>-<manager[-vX]>-<mode>-<prefix>-<hash>
CACHE_KEY="${{ runner.os }}-${MANAGER_SUFFIX}-${MODE_SUFFIX}-${{ inputs.cache-prefix }}-${CACHE_HASH_SHORT}"
# 恢复前缀:用于 restore-keys防止不同包管理器/模式的回退误命中
RESTORE_PREFIX="${{ runner.os }}-${MANAGER_SUFFIX}-${MODE_SUFFIX}-${{ inputs.cache-prefix }}-"
echo "key=${CACHE_KEY}" >> $GITHUB_OUTPUT
echo "restore-prefix=${RESTORE_PREFIX}" >> $GITHUB_OUTPUT
echo "使用hash: ${CACHE_HASH}"
echo "缓存key: ${CACHE_KEY}"
- name: 缓存 pnpm 二进制 (Corepack)
if: inputs.package-manager == 'pnpm'
uses: actions/cache@v4
with:
path: |
~/.cache/corepack
key: ${{ runner.os }}-corepack-pnpm-${{ inputs.pnpm-version }}
restore-keys: |
${{ runner.os }}-corepack-pnpm-
- name: 确保 pnpm 可用
if: inputs.package-manager == 'pnpm'
uses: pnpm/action-setup@v4
with:
version: ${{ inputs.pnpm-version }}
run_install: false
- name: 确定缓存路径
id: cache-path
shell: bash
run: |
MODE="${{ inputs.cache-mode }}"
MANAGER="${{ inputs.package-manager }}"
if [[ -z "$MODE" ]]; then MODE="node_modules"; fi
if [[ "$MODE" == "node_modules" ]]; then
CACHE_PATH="${{ inputs.node-modules-path }}"
else
case "$MANAGER" in
"npm")
# npm 的全局缓存目录
CACHE_PATH="$HOME/.npm"
;;
"pnpm")
# pnpm store 路径(通过命令获取,以兼容自定义配置)
if command -v pnpm >/dev/null 2>&1; then
CACHE_PATH="$(pnpm store path)"
else
# 回退:若未能获取,则使用常见默认路径
CACHE_PATH="$HOME/.pnpm-store"
fi
;;
"yarn")
# yarn v1 默认缓存目录yarn berry 采用不同机制,这里聚焦 v1 常见场景)
CACHE_PATH="$HOME/.cache/yarn"
;;
*)
echo "❌ 不支持的包管理器: $MANAGER"
exit 1
;;
esac
fi
echo "path=${CACHE_PATH}" >> $GITHUB_OUTPUT
- name: 拉取缓存依赖
id: cache
uses: actions/cache@v4
with:
path: ${{ steps.cache-path.outputs.path }}
key: ${{ steps.cache-key.outputs.key }}
restore-keys: |
${{ steps.cache-key.outputs.restore-prefix }}
- name: 显示缓存状态
shell: bash
run: |
if [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]]; then
if [[ "${{ inputs.cache-mode }}" == "store" ]]; then
echo "✅ 缓存命中store将执行快速链接安装不会下载包仅链接"
else
echo "✅ 缓存命中,跳过依赖安装"
fi
else
echo "⚠️ 缓存未命中,开始安装依赖"
fi
- name: 安装依赖
if: (inputs.cache-mode == 'node_modules' && steps.cache.outputs.cache-hit != 'true') || (inputs.cache-mode == 'store')
shell: bash
run: |
# 如果提供了自定义安装命令,使用自定义命令
if [[ -n "${{ inputs.install-command }}" ]]; then
echo "🔧 使用自定义安装命令: ${{ inputs.install-command }}"
${{ inputs.install-command }}
else
INSTALL_ARGS="${{ inputs.install-args }}"
if [[ -n "$INSTALL_ARGS" ]]; then
echo " 附加安装参数: $INSTALL_ARGS"
fi
# 根据模式与缓存命中优化安装参数(尽量离线加速)
EXTRA_FLAGS=""
if [[ "${{ inputs.optimize-install-flags }}" == "true" && "${{ inputs.package-manager }}" == "pnpm" && "${{ inputs.cache-mode }}" == "store" ]]; then
if [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]]; then
# 缓存命中:使用完全离线与锁定安装,避免网络请求
EXTRA_FLAGS="--offline --frozen-lockfile"
else
# 缓存未命中:尽量离线,但允许必要网络;同时锁定避免解析差异
EXTRA_FLAGS="--prefer-offline --frozen-lockfile"
fi
echo "⚡ pnpm安装优化参数: $EXTRA_FLAGS"
fi
# 根据包管理器选择安装命令
case "${{ inputs.package-manager }}" in
"npm")
if [[ "${{ inputs.force-install }}" == "true" ]]; then
echo "🔧 使用npm强制安装"
npm install --force ${INSTALL_ARGS}
else
echo "🔧 使用npm安装"
npm install ${INSTALL_ARGS}
fi
;;
"pnpm")
if [[ "${{ inputs.force-install }}" == "true" ]]; then
echo "🔧 使用pnpm强制安装"
pnpm install --force ${EXTRA_FLAGS} ${INSTALL_ARGS}
else
echo "🔧 使用pnpm安装"
pnpm install ${EXTRA_FLAGS} ${INSTALL_ARGS}
fi
;;
"yarn")
if [[ "${{ inputs.force-install }}" == "true" ]]; then
echo "🔧 使用yarn强制安装"
yarn install --force ${INSTALL_ARGS}
else
echo "🔧 使用yarn安装"
yarn install ${INSTALL_ARGS}
fi
;;
*)
echo "❌ 不支持的包管理器: ${{ inputs.package-manager }}"
exit 1
;;
esac
fi
echo "✅ 依赖安装完成"
- name: 执行Git Stash
if: steps.cache.outputs.cache-hit != 'true' && inputs.enable-git-stash == 'true'
shell: bash
run: |
echo "🔄 执行git stash..."
git stash
echo "✅ git stash完成"
- name: 安装总结
shell: bash
run: |
echo "📦 依赖安装总结:"
echo " - 包管理器: ${{ inputs.package-manager }}"
echo " - 缓存命中: ${{ steps.cache.outputs.cache-hit }}"
echo " - 缓存key: ${{ steps.cache-key.outputs.key }}"
if [[ "${{ steps.cache.outputs.cache-hit }}" != "true" ]]; then
echo " - 强制安装: ${{ inputs.force-install }}"
echo " - Git Stash: ${{ inputs.enable-git-stash }}"
fi