name: 'pnpm依赖安装与缓存' description: '专注于pnpm的依赖缓存与安装,加速重复执行' branding: icon: 'package' color: 'yellow' inputs: cache-mode: description: '缓存模式 (node_modules 或 store)' required: false default: 'node_modules' cache-prefix: description: '缓存前缀名称' required: false default: 'modules' node-modules-path: description: 'node_modules目录路径(cache-mode=node_modules 时生效)' required: false default: 'node_modules' force-install: description: '是否强制安装 (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: '' clean-project-store: description: '安装后清理项目根目录的 .pnpm-store (true/false)' required: false default: 'false' 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: 检查pnpm id: detect shell: bash run: | if ! command -v pnpm >/dev/null 2>&1; then echo "pnpm 未安装" exit 1 fi VERSION=$(pnpm --version | tr -d '\n') if [[ -z "$VERSION" ]]; then echo "无法获取pnpm版本" exit 1 fi echo "pnpm-version=${VERSION}" >> "$GITHUB_OUTPUT" echo "PNPM_VERSION=${VERSION}" >> "$GITHUB_ENV" - name: 生成缓存key id: cache-key shell: bash env: PNPM_VERSION: ${{ steps.detect.outputs.pnpm-version }} FALLBACK_HASH: ${{ hashFiles('pnpm-lock.yaml') }} run: | set -euo pipefail if [[ -n "${{ inputs.cache-hash }}" ]]; then CACHE_HASH="${{ inputs.cache-hash }}" elif [[ -n "${FALLBACK_HASH}" ]]; then CACHE_HASH="${FALLBACK_HASH}" else CACHE_HASH="" fi if [[ -n "$CACHE_HASH" ]]; then CACHE_HASH_SHORT=$(echo "$CACHE_HASH" | head -c 12) else CACHE_HASH_SHORT="no-hash" fi PNPM_VERSION="${PNPM_VERSION}" MODE="${{ inputs.cache-mode }}" if [[ -z "$MODE" ]]; then MODE="node_modules" fi CACHE_KEY="${{ runner.os }}-pnpm-v${PNPM_VERSION}-${MODE}-${{ inputs.cache-prefix }}-${CACHE_HASH_SHORT}" RESTORE_PREFIX="${{ runner.os }}-pnpm-v${PNPM_VERSION}-${MODE}-${{ inputs.cache-prefix }}-" echo "key=${CACHE_KEY}" >> "$GITHUB_OUTPUT" echo "restore-prefix=${RESTORE_PREFIX}" >> "$GITHUB_OUTPUT" echo "hash=${CACHE_HASH}" >> "$GITHUB_OUTPUT" - name: 确定缓存路径 id: cache-path shell: bash run: | set -euo pipefail MODE="${{ inputs.cache-mode }}" if [[ -z "$MODE" ]]; then MODE="node_modules" fi if [[ "$MODE" == "node_modules" ]]; then CACHE_PATH="${{ inputs.node-modules-path }}" STORE_DIR="${RUNNER_TEMP:-$HOME}/.pnpm-store" else STORE_DIR="${RUNNER_TEMP:-$HOME}/.pnpm-store" CACHE_PATH="$STORE_DIR" fi mkdir -p "$STORE_DIR" echo "PNPM_STORE_DIR=${STORE_DIR}" >> "$GITHUB_ENV" 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 echo "缓存命中" else echo "缓存未命中" fi - name: 安装依赖 if: (inputs.cache-mode == 'node_modules' && steps.cache.outputs.cache-hit != 'true') || (inputs.cache-mode == 'store') shell: bash run: | set -euo pipefail export PNPM_STORE_DIR="${PNPM_STORE_DIR:-${RUNNER_TEMP:-$HOME}/.pnpm-store}" export npm_config_store_dir="$PNPM_STORE_DIR" export PNPM_CONFIG_STORE_DIR="$PNPM_STORE_DIR" pnpm config set store-dir "$PNPM_STORE_DIR" --location=project || true if [[ "${{ inputs.cache-mode }}" == "node_modules" ]]; then export PNPM_NODE_LINKER="hoisted" pnpm config set node-linker hoisted --location=project || true pnpm config set node-linker hoisted --location=global || true echo "🔧 已将 pnpm node-linker 设置为 hoisted,避免 symlink" fi if [[ -n "${{ inputs.install-command }}" ]]; then echo "🔧 使用自定义安装命令: ${{ inputs.install-command }}" eval "${{ inputs.install-command }}" exit 0 fi FLAGS=("--frozen-lockfile") if [[ "${{ inputs.cache-mode }}" == "store" ]]; then if [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]]; then FLAGS=("--offline" "--frozen-lockfile") else FLAGS=("--prefer-offline" "--frozen-lockfile") fi fi if [[ "${{ inputs.force-install }}" == "true" ]]; then FLAGS+=("--force") fi INSTALL_ARGS="${{ inputs.install-args }}" echo "🔧 执行 pnpm install ${FLAGS[*]} ${INSTALL_ARGS}" if [[ -n "$INSTALL_ARGS" ]]; then pnpm install "${FLAGS[@]}" $INSTALL_ARGS else pnpm install "${FLAGS[@]}" fi if [[ "${{ inputs.clean-project-store }}" == "true" && -d ".pnpm-store" && "${PNPM_STORE_DIR}" != "$PWD/.pnpm-store" ]]; then rm -rf .pnpm-store || true fi - name: 总结 shell: bash run: | echo "pnpm版本: $PNPM_VERSION" echo "缓存命中: ${{ steps.cache.outputs.cache-hit }}" echo "缓存key: ${{ steps.cache-key.outputs.key }}"