feat: 新增 setup-opencode action,支持 npm 全局安装和智能缓存

- 使用 npm 全局安装 OpenCode,支持版本管理和自动更新
- 智能缓存策略:缓存 npm 目录和全局安装,加速后续构建
- 支持淘宝镜像源和自定义 npm 镜像,国内环境友好
- 版本检测:自动比对已安装版本,避免重复安装
- 输出安装版本、缓存命中状态和更新状态
- 提供详细的使用文档和多场景示例
This commit is contained in:
Lyda
2026-03-17 16:17:03 +08:00
parent 1618c6fe6d
commit 489cc94316
6 changed files with 724 additions and 0 deletions
+295
View File
@@ -0,0 +1,295 @@
name: 'Setup OpenCode'
description: '使用 npm 全局安装 OpenCode 并缓存,支持版本检测和自动更新'
author: 'Your Organization'
branding:
icon: 'code'
color: 'purple'
inputs:
version:
description: 'OpenCode 版本号(例如: 1.0.0, latest'
required: false
default: 'latest'
package-name:
description: 'npm 包名'
required: false
default: 'opencode'
use-taobao-registry:
description: '使用淘宝 npm 镜像源 (true/false)'
required: false
default: 'true'
npm-registry:
description: '自定义 npm 镜像源地址'
required: false
default: ''
cache-prefix:
description: '缓存前缀名称'
required: false
default: 'opencode-npm'
skip-cache:
description: '跳过缓存,强制重新安装 (true/false)'
required: false
default: 'false'
outputs:
version:
description: '安装的 OpenCode 版本'
value: ${{ steps.get-version.outputs.version }}
cache-hit:
description: '缓存是否命中 (true/false)'
value: ${{ steps.cache-restore.outputs.cache-hit }}
updated:
description: '是否执行了更新 (true/false)'
value: ${{ steps.check-update.outputs.updated }}
runs:
using: 'composite'
steps:
- name: 验证输入参数
shell: bash
run: |
echo "🔍 验证输入参数..."
if [[ -z "${{ inputs.version }}" ]]; then
echo "❌ version 参数不能为空"
exit 1
fi
if [[ -z "${{ inputs.package-name }}" ]]; then
echo "❌ package-name 参数不能为空"
exit 1
fi
echo "✅ 输入参数验证通过"
echo " - npm 包名: ${{ inputs.package-name }}"
echo " - 版本: ${{ inputs.version }}"
echo " - 使用淘宝镜像: ${{ inputs.use-taobao-registry }}"
echo " - 跳过缓存: ${{ inputs.skip-cache }}"
- name: 配置 npm 镜像源
id: setup-registry
shell: bash
run: |
if [[ -n "${{ inputs.npm-registry }}" ]]; then
echo "🔧 使用自定义 npm 镜像源"
NPM_REGISTRY="${{ inputs.npm-registry }}"
echo " - 镜像源: ${NPM_REGISTRY}"
elif [[ "${{ inputs.use-taobao-registry }}" == "true" ]]; then
echo "🇨🇳 使用淘宝 npm 镜像源"
NPM_REGISTRY="https://registry.npmmirror.com"
echo " - 镜像源: ${NPM_REGISTRY}"
else
echo "🌍 使用默认 npm 镜像源"
NPM_REGISTRY=""
fi
echo "registry=${NPM_REGISTRY}" >> $GITHUB_OUTPUT
- name: 获取 npm 路径
id: npm-paths
shell: bash
run: |
NPM_PREFIX=$(npm config get prefix)
NPM_GLOBAL_ROOT=$(npm root -g)
echo "prefix=${NPM_PREFIX}" >> $GITHUB_OUTPUT
echo "global-root=${NPM_GLOBAL_ROOT}" >> $GITHUB_OUTPUT
echo "📁 npm 全局路径: ${NPM_PREFIX}"
echo "📦 npm 全局包路径: ${NPM_GLOBAL_ROOT}"
- name: 解析版本号
id: resolve-version
shell: bash
env:
NPM_CONFIG_REGISTRY: ${{ steps.setup-registry.outputs.registry }}
run: |
VERSION="${{ inputs.version }}"
PACKAGE="${{ inputs.package-name }}"
if [[ "${VERSION}" == "latest" ]]; then
echo "🔍 获取最新版本号..."
LATEST_VERSION=$(npm view ${PACKAGE} version 2>/dev/null || echo "")
if [[ -n "${LATEST_VERSION}" ]]; then
VERSION="${LATEST_VERSION}"
echo "✅ 最新版本: ${VERSION}"
else
echo "⚠️ 无法获取最新版本,使用 latest 标签"
VERSION="latest"
fi
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "📌 目标版本: ${VERSION}"
- name: 生成缓存键
id: cache-key
shell: bash
run: |
VERSION="${{ steps.resolve-version.outputs.version }}"
PACKAGE="${{ inputs.package-name }}"
CACHE_KEY="${{ runner.os }}-${{ inputs.cache-prefix }}-${PACKAGE}-${VERSION}"
echo "key=${CACHE_KEY}" >> $GITHUB_OUTPUT
echo "🔑 缓存键: ${CACHE_KEY}"
- name: 恢复缓存
id: cache-restore
if: inputs.skip-cache != 'true'
uses: actions/cache/restore@v4
with:
path: |
~/.npm
${{ steps.npm-paths.outputs.global-root }}/${{ inputs.package-name }}
${{ steps.npm-paths.outputs.prefix }}/bin/${{ inputs.package-name }}
key: ${{ steps.cache-key.outputs.key }}
restore-keys: |
${{ runner.os }}-${{ inputs.cache-prefix }}-${{ inputs.package-name }}-
- name: 检查已安装版本
id: check-installed
shell: bash
run: |
PACKAGE="${{ inputs.package-name }}"
TARGET_VERSION="${{ steps.resolve-version.outputs.version }}"
NEED_INSTALL="true"
if command -v $PACKAGE &> /dev/null; then
# 使用 JSON 格式获取版本信息,更可靠
INSTALLED_INFO=$(npm list -g $PACKAGE --json --depth=0 2>/dev/null || echo '{}')
INSTALLED_VERSION=$(echo "${INSTALLED_INFO}" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4 || echo "unknown")
echo "📦 已安装版本: ${INSTALLED_VERSION}"
echo "🎯 目标版本: ${TARGET_VERSION}"
if [[ "${INSTALLED_VERSION}" == "${TARGET_VERSION}" ]]; then
NEED_INSTALL="false"
echo "✅ 版本匹配,无需重新安装"
elif [[ "${INSTALLED_VERSION}" != "unknown" ]]; then
echo "⚠️ 版本不匹配,需要更新"
fi
else
echo "⚠️ 未安装 ${PACKAGE}"
fi
echo "need-install=${NEED_INSTALL}" >> $GITHUB_OUTPUT
- name: 安装 OpenCode
if: steps.check-installed.outputs.need-install == 'true'
shell: bash
env:
NPM_CONFIG_REGISTRY: ${{ steps.setup-registry.outputs.registry }}
run: |
PACKAGE="${{ inputs.package-name }}"
VERSION="${{ steps.resolve-version.outputs.version }}"
INSTALL_SPEC="${PACKAGE}@${VERSION}"
echo "📥 安装 ${INSTALL_SPEC}..."
# 重试机制:最多尝试 3 次
MAX_RETRIES=3
RETRY_COUNT=0
while [[ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]]; do
if npm install -g "${INSTALL_SPEC}" --no-audit --no-fund; then
echo "✅ 安装成功"
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [[ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]]; then
echo "⚠️ 安装失败,等待 3 秒后重试 (${RETRY_COUNT}/${MAX_RETRIES})..."
sleep 3
else
echo "❌ 安装失败,已重试 ${MAX_RETRIES} 次"
exit 1
fi
fi
done
- name: 验证安装
id: verify-install
if: steps.check-installed.outputs.need-install == 'true'
shell: bash
run: |
PACKAGE="${{ inputs.package-name }}"
if command -v $PACKAGE &> /dev/null; then
echo "install-verified=true" >> $GITHUB_OUTPUT
echo "✅ 安装验证成功"
else
echo "install-verified=false" >> $GITHUB_OUTPUT
echo "❌ 安装验证失败"
exit 1
fi
- name: 保存缓存
if: steps.check-installed.outputs.need-install == 'true' && steps.verify-install.outputs.install-verified == 'true' && inputs.skip-cache != 'true'
uses: actions/cache/save@v4
with:
path: |
~/.npm
${{ steps.npm-paths.outputs.global-root }}/${{ inputs.package-name }}
${{ steps.npm-paths.outputs.prefix }}/bin/${{ inputs.package-name }}
key: ${{ steps.cache-key.outputs.key }}
- name: 获取安装版本
id: get-version
shell: bash
run: |
PACKAGE="${{ inputs.package-name }}"
if command -v $PACKAGE &> /dev/null; then
# 使用 JSON 格式获取版本,更可靠
INSTALLED_INFO=$(npm list -g $PACKAGE --json --depth=0 2>/dev/null || echo '{}')
VERSION=$(echo "${INSTALLED_INFO}" | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4 || echo "unknown")
if [[ "${VERSION}" == "unknown" ]]; then
# 备用方案:直接运行命令获取版本
VERSION=$($PACKAGE --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "✅ OpenCode 版本: ${VERSION}"
else
echo "version=unknown" >> $GITHUB_OUTPUT
echo "⚠️ 无法获取 OpenCode 版本"
fi
- name: 检查更新状态
id: check-update
shell: bash
run: |
if [[ "${{ steps.check-installed.outputs.need-install }}" == "true" ]]; then
echo "updated=true" >> $GITHUB_OUTPUT
echo "🔄 已安装新版本"
else
echo "updated=false" >> $GITHUB_OUTPUT
echo "📌 使用已安装版本,未执行更新"
fi
- name: 安装总结
shell: bash
run: |
echo "📊 OpenCode 安装总结:"
echo " - npm 包名: ${{ inputs.package-name }}"
echo " - 安装版本: ${{ steps.get-version.outputs.version }}"
echo " - 缓存命中: ${{ steps.cache-restore.outputs.cache-hit }}"
echo " - 执行更新: ${{ steps.check-update.outputs.updated }}"
echo " - 缓存键名: ${{ steps.cache-key.outputs.key }}"
if [[ "${{ steps.check-update.outputs.updated }}" == "true" ]]; then
echo " 🔄 已通过 npm 安装新版本"
else
echo " ✅ 使用已安装版本,跳过安装"
fi
echo ""
echo "🎉 OpenCode 已准备就绪!"