16 Commits

Author SHA1 Message Date
Lyda 595d906213 Merge branch 'v1' 2026-03-11 11:01:23 +08:00
Lyda ff48b8a3e8 Merge branch 'v1' 2025-11-17 16:00:00 +08:00
Lyda 520bc406f4 Merge branch 'v1' 2025-08-21 12:49:53 +08:00
Lyda d4720de1a5 Merge branch 'v1' 2025-08-21 12:32:18 +08:00
Lyda cad99bbfc5 Merge branch 'v1' 2025-08-21 12:23:08 +08:00
Lyda df3a7e1eba Merge branch 'v1' 2025-08-21 12:00:25 +08:00
Lyda dc525a04b6 Merge branch 'v1' 2025-08-21 11:59:21 +08:00
Lyda 1aa9b20a79 Merge branch 'v1' 2025-08-21 10:14:39 +08:00
Lyda 4609366c51 Merge branch 'v1' 2025-08-20 20:38:43 +08:00
Lyda 3d246034c0 Merge branch 'v1' 2025-08-20 19:30:03 +08:00
Lyda bf094b92dd Merge branch 'v1' 2025-08-20 18:39:48 +08:00
Lyda b3ca961260 Merge branch 'v1' 2025-08-20 17:53:53 +08:00
Lyda ea1c69a371 Merge branch 'v1' 2025-08-20 17:38:25 +08:00
Lyda dfcf7e8c22 Merge branch 'v1' 2025-08-20 17:26:23 +08:00
Lyda 017d6705a2 Merge branch 'v1' 2025-08-20 17:13:06 +08:00
Lyda 12d3d5dc0d feat: 更新 npm-install GitHub Action,重构锁文件处理逻辑,优化缓存key生成步骤,增加对锁文件不存在的警告提示。 2025-08-20 15:16:57 +08:00
10 changed files with 87 additions and 1160 deletions
-261
View File
@@ -1,261 +0,0 @@
name: Test Setup OpenCode Action
on:
push:
branches: [ main, develop ]
paths:
- 'setup-opencode/**'
- '.github/workflows/test-setup-opencode.yml'
pull_request:
branches: [ main ]
paths:
- 'setup-opencode/**'
workflow_dispatch:
jobs:
test-basic-install:
name: 测试基础安装
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode
id: setup
uses: ./setup-opencode
with:
version: 'latest'
- name: 验证安装
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
echo "缓存命中: ${{ steps.setup.outputs.cache-hit }}"
echo "执行更新: ${{ steps.setup.outputs.updated }}"
# 验证命令是否可用
if command -v opencode &> /dev/null; then
echo "✅ opencode 命令可用"
opencode --version || echo "⚠️ 无法获取版本信息(这是正常的,因为 opencode 可能不是真实的包)"
else
echo "❌ opencode 命令不可用"
exit 1
fi
test-specific-version:
name: 测试指定版本
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode 指定版本
id: setup
uses: ./setup-opencode
with:
version: '1.0.0'
- name: 验证版本
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
if [[ "${{ steps.setup.outputs.version }}" == "1.0.0" ]]; then
echo "✅ 版本匹配"
else
echo "⚠️ 版本不匹配,但这可能是因为包不存在"
fi
test-taobao-registry:
name: 测试淘宝镜像源
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 使用淘宝镜像安装
id: setup
uses: ./setup-opencode
with:
version: 'latest'
use-taobao-registry: 'true'
- name: 验证安装
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
echo "缓存命中: ${{ steps.setup.outputs.cache-hit }}"
test-custom-registry:
name: 测试自定义镜像源
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 使用自定义镜像源
id: setup
uses: ./setup-opencode
with:
version: 'latest'
npm-registry: 'https://registry.npmmirror.com'
- name: 验证安装
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
test-cache:
name: 测试缓存功能
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 第一次安装
id: first-install
uses: ./setup-opencode
with:
version: 'latest'
- name: 验证第一次安装
run: |
echo "第一次安装 - 缓存命中: ${{ steps.first-install.outputs.cache-hit }}"
echo "第一次安装 - 执行更新: ${{ steps.first-install.outputs.updated }}"
if [[ "${{ steps.first-install.outputs.cache-hit }}" == "true" ]]; then
echo "✅ 缓存命中(可能是之前的运行)"
else
echo "✅ 首次安装,未命中缓存"
fi
- name: 第二次安装(应该使用缓存)
id: second-install
uses: ./setup-opencode
with:
version: 'latest'
- name: 验证第二次安装
run: |
echo "第二次安装 - 缓存命中: ${{ steps.second-install.outputs.cache-hit }}"
echo "第二次安装 - 执行更新: ${{ steps.second-install.outputs.updated }}"
if [[ "${{ steps.second-install.outputs.updated }}" == "false" ]]; then
echo "✅ 正确使用了已安装的版本"
else
echo "⚠️ 重新安装了(可能版本不同)"
fi
test-skip-cache:
name: 测试跳过缓存
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 跳过缓存安装
id: setup
uses: ./setup-opencode
with:
version: 'latest'
skip-cache: 'true'
- name: 验证安装
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
echo "缓存命中: ${{ steps.setup.outputs.cache-hit }}"
if [[ "${{ steps.setup.outputs.cache-hit }}" == "false" || "${{ steps.setup.outputs.cache-hit }}" == "" ]]; then
echo "✅ 正确跳过了缓存"
else
echo "⚠️ 未跳过缓存"
fi
test-custom-package:
name: 测试自定义包名
runs-on: ci-node-22
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装真实的 npm 包(用于测试)
id: setup
uses: ./setup-opencode
with:
package-name: 'cowsay'
version: 'latest'
- name: 验证安装
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
if command -v cowsay &> /dev/null; then
echo "✅ cowsay 安装成功"
cowsay "Setup OpenCode Action 测试成功!"
else
echo "❌ cowsay 未安装"
exit 1
fi
test-matrix:
name: 测试多版本矩阵
runs-on: ci-node-22
strategy:
matrix:
node-version: [18, 20]
package: ['cowsay', 'figlet']
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: 安装 ${{ matrix.package }}
id: setup
uses: ./setup-opencode
with:
package-name: ${{ matrix.package }}
version: 'latest'
- name: 验证安装
run: |
echo "Node.js 版本: $(node --version)"
echo "npm 版本: $(npm --version)"
echo "安装的包: ${{ matrix.package }}"
echo "包版本: ${{ steps.setup.outputs.version }}"
if command -v ${{ matrix.package }} &> /dev/null; then
echo "✅ ${{ matrix.package }} 安装成功"
else
echo "❌ ${{ matrix.package }} 未安装"
exit 1
fi
test-summary:
name: 测试总结
runs-on: ci-node-22
needs:
- test-basic-install
- test-specific-version
- test-taobao-registry
- test-custom-registry
- test-cache
- test-skip-cache
- test-custom-package
- test-matrix
if: always()
steps:
- name: 测试结果总结
run: |
echo "## 🎉 Setup OpenCode Action 测试完成"
echo ""
echo "### 测试项目:"
echo "- ✅ 基础安装"
echo "- ✅ 指定版本"
echo "- ✅ 淘宝镜像源"
echo "- ✅ 自定义镜像源"
echo "- ✅ 缓存功能"
echo "- ✅ 跳过缓存"
echo "- ✅ 自定义包名"
echo "- ✅ 多版本矩阵"
echo ""
echo "所有测试已执行完成!"
+31 -35
View File
@@ -1,58 +1,58 @@
name: "Configure Build Environment"
description: "验证已有环境并配置 Git 与 kubectl(不执行软件安装)"
author: "Your Organization"
name: 'Configure Build Environment'
description: '验证已有环境并配置 Git 与 kubectl(不执行软件安装)'
author: 'Your Organization'
branding:
icon: "settings"
color: "green"
icon: 'settings'
color: 'green'
inputs:
git-user-name:
description: "Git 用户名"
description: 'Git 用户名'
required: false
default: "GiteaActions"
default: 'GiteaActions'
git-user-email:
description: "Git 用户邮箱"
description: 'Git 用户邮箱'
required: false
default: "xgj-actions@xmail.bjxgj.com"
default: 'xgj-actions@xmail.bjxgj.com'
kube-config:
description: "Base64 编码的 kubectl 配置文件"
description: 'Base64 编码的 kubectl 配置文件'
required: false
default: ""
default: ''
enable-validation:
description: "是否执行环境校验 (true/false)"
description: '是否执行环境校验 (true/false)'
required: false
default: "false"
default: 'true'
docker-registry:
description: "Docker 私有仓库地址"
description: 'Docker 私有仓库地址'
required: false
default: "docker-registry.bjxgj.com"
default: 'docker-registry.bjxgj.com'
docker-username:
description: "Docker 仓库用户名"
description: 'Docker 仓库用户名'
required: false
default: "GiteaDocker"
default: 'GiteaDocker'
docker-password:
description: "Docker 仓库密码(开启登录时必填)"
description: 'Docker 仓库密码(开启登录时必填)'
required: false
default: ""
default: ''
skip-docker-login:
description: "是否跳过 Docker 登录 (true/false)"
description: '是否跳过 Docker 登录 (true/false)'
required: false
default: "false"
default: 'false'
outputs:
docker-version:
description: "检测到的 Docker 版本"
description: '检测到的 Docker 版本'
value: ${{ steps.validate-tools.outputs.docker-version }}
kubectl-version:
description: "检测到的 kubectl 版本"
description: '检测到的 kubectl 版本'
value: ${{ steps.validate-tools.outputs.kubectl-version }}
kubectl-context:
description: "验证通过时的当前 kubectl 上下文"
description: '验证通过时的当前 kubectl 上下文'
value: ${{ steps.verify-kubectl.outputs.current-context }}
runs:
using: "composite"
using: 'composite'
steps:
- name: 配置 Git
shell: bash
@@ -69,7 +69,7 @@ runs:
ENABLE_VALIDATION: ${{ inputs.enable-validation }}
- name: 配置 kubectl
if: ${{ inputs.kube-config != '' && steps.validate-tools.outputs.kubectl-version != 'not-found' }}
if: ${{ inputs.kube-config != '' }}
shell: bash
run: bash ${{ github.action_path }}/scripts/configure-kubectl.sh
env:
@@ -77,12 +77,12 @@ runs:
- name: 验证 kubectl 连通性
id: verify-kubectl
if: ${{ inputs.kube-config != '' && inputs.enable-validation != 'false' && steps.validate-tools.outputs.kubectl-version != 'not-found' && steps.validate-tools.outputs.kubectl-version != 'skipped' }}
if: ${{ inputs.kube-config != '' && inputs.enable-validation != 'false' }}
shell: bash
run: bash ${{ github.action_path }}/scripts/verify-kubectl.sh
- name: 登录私有 Docker 仓库
if: ${{ inputs.skip-docker-login != 'true' && steps.validate-tools.outputs.docker-version != 'not-found' }}
if: ${{ inputs.skip-docker-login != 'true' }}
uses: docker/login-action@v3
with:
registry: ${{ inputs.docker-registry }}
@@ -94,13 +94,9 @@ runs:
run: |
echo '🎉 环境校验与配置步骤完成'
if [[ "${{ inputs.enable-validation }}" != 'false' ]]; then
if [[ "${{ steps.validate-tools.outputs.docker-version }}" != 'not-found' && "${{ steps.validate-tools.outputs.docker-version }}" != 'skipped' ]]; then
echo " - Docker: ${{ steps.validate-tools.outputs.docker-version }}"
fi
if [[ "${{ steps.validate-tools.outputs.kubectl-version }}" != 'not-found' && "${{ steps.validate-tools.outputs.kubectl-version }}" != 'skipped' ]]; then
echo " - kubectl: ${{ steps.validate-tools.outputs.kubectl-version }}"
fi
echo " - Docker: ${{ steps.validate-tools.outputs.docker-version }}"
echo " - kubectl: ${{ steps.validate-tools.outputs.kubectl-version }}"
fi
if [[ "${{ inputs.kube-config }}" != '' && "${{ inputs.enable-validation }}" != 'false' && "${{ steps.verify-kubectl.outputs.current-context }}" != '' ]]; then
if [[ "${{ inputs.kube-config }}" != '' && "${{ inputs.enable-validation }}" != 'false' ]]; then
echo " - 当前上下文: ${{ steps.verify-kubectl.outputs.current-context }}"
fi
+1 -2
View File
@@ -58,7 +58,7 @@ validate_binary() {
main() {
: "${GITHUB_OUTPUT:?GITHUB_OUTPUT 未设置}" >/dev/null
local enable_validation="${ENABLE_VALIDATION:-false}"
local enable_validation="${ENABLE_VALIDATION:-true}"
if [[ "$enable_validation" != "true" ]]; then
log_info "已通过统一开关禁用环境校验"
@@ -67,7 +67,6 @@ main() {
return 0
fi
# 当 enable-validation 为 true 时,Docker 和 kubectl 都是必需的
validate_binary "docker" "true" "docker --version" "docker-version"
validate_binary "kubectl" "true" "kubectl version --client --short 2>/dev/null || kubectl version --client" "kubectl-version"
}
+55 -145
View File
@@ -6,14 +6,9 @@ branding:
inputs:
cache-prefix:
description: '缓存前缀名称(留空则自动使用项目名)'
description: '缓存前缀名称'
required: false
default: ''
cache-hash:
description: '缓存hash值(留空则自动使用 pnpm-lock.yaml 或 package.json'
required: false
default: ''
default: 'modules'
force-install:
description: '是否强制安装 (true/false)'
@@ -30,10 +25,10 @@ inputs:
required: false
default: ''
strict-lockfile-check:
description: '严格检查 lockfile 是否被修改 (true/false)'
cache-hash:
description: '缓存hash值(推荐使用hashFiles'
required: false
default: 'true'
default: ''
clean-project-store:
description: '安装后清理项目根目录的 .pnpm-store (true/false)'
@@ -60,17 +55,15 @@ runs:
id: detect
shell: bash
run: |
set -euo pipefail
if ! command -v pnpm >/dev/null 2>&1; then
echo "pnpm 未安装,请先使用 pnpm/action-setup 安装" >&2
echo "pnpm 未安装"
exit 1
fi
VERSION=$(pnpm --version 2>/dev/null | tr -d '\n' | tr -d '\r')
VERSION=$(pnpm --version | tr -d '\n')
if [[ -z "$VERSION" ]]; then
echo "无法获取 pnpm 版本" >&2
echo "无法获取pnpm版本"
exit 1
fi
echo "✅ 检测到 pnpm 版本: $VERSION"
echo "pnpm-version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "PNPM_VERSION=${VERSION}" >> "$GITHUB_ENV"
@@ -79,60 +72,27 @@ runs:
shell: bash
env:
PNPM_VERSION: ${{ steps.detect.outputs.pnpm-version }}
LOCKFILE_HASH: ${{ hashFiles('**/pnpm-lock.yaml') }}
PACKAGE_HASH: ${{ hashFiles('**/package.json') }}
FALLBACK_HASH: ${{ hashFiles('pnpm-lock.yaml') }}
PACKAGE_HASH: ${{ hashFiles('package.json') }}
run: |
set -euo pipefail
# 智能选择最佳 hash 策略
if [[ -n "${{ inputs.cache-hash }}" ]]; then
CACHE_HASH="${{ inputs.cache-hash }}"
HASH_SOURCE="custom"
elif [[ -n "${LOCKFILE_HASH}" ]]; then
CACHE_HASH="${LOCKFILE_HASH}"
HASH_SOURCE="pnpm-lock.yaml"
elif [[ -n "${FALLBACK_HASH}" ]]; then
CACHE_HASH="${FALLBACK_HASH}"
elif [[ -n "${PACKAGE_HASH}" ]]; then
CACHE_HASH="${PACKAGE_HASH}"
HASH_SOURCE="package.json"
else
CACHE_HASH=""
HASH_SOURCE="none"
fi
# 生成简短的 hash 用于 key
if [[ -n "$CACHE_HASH" ]]; then
CACHE_HASH_SHORT=$(echo "$CACHE_HASH" | head -c 12)
else
CACHE_HASH_SHORT="no-lock"
CACHE_HASH_SHORT="no-hash"
fi
# 智能获取缓存前缀
if [[ -n "${{ inputs.cache-prefix }}" ]]; then
CACHE_PREFIX="${{ inputs.cache-prefix }}"
else
# 获取当前工作目录相对于仓库根目录的路径,避免 monorepo 子包冲突
REPO_ROOT="$GITHUB_WORKSPACE"
CURRENT_DIR="$(pwd)"
# 计算相对路径
if [[ "$CURRENT_DIR" == "$REPO_ROOT" ]]; then
# 在仓库根目录,使用仓库名
CACHE_PREFIX=$(basename "$REPO_ROOT" 2>/dev/null || echo "project")
else
# 在子目录,使用相对路径(替换 / 为 -)
REL_PATH="${CURRENT_DIR#$REPO_ROOT/}"
CACHE_PREFIX=$(echo "$REL_PATH" | tr '/' '-')
fi
fi
# 构建缓存 key
CACHE_KEY="${{ runner.os }}-${CACHE_PREFIX}-pnpm-v${PNPM_VERSION}-${CACHE_HASH_SHORT}"
RESTORE_PREFIX="${{ runner.os }}-${CACHE_PREFIX}-pnpm-v${PNPM_VERSION}-"
echo "📝 缓存 key: ${CACHE_KEY}"
echo "📝 Hash 来源: ${HASH_SOURCE} (${CACHE_HASH_SHORT})"
echo "📝 缓存前缀: ${CACHE_PREFIX}"
PNPM_VERSION="${PNPM_VERSION}"
CACHE_KEY="${{ runner.os }}-pnpm-v${PNPM_VERSION}-store-${{ inputs.cache-prefix }}-${CACHE_HASH_SHORT}"
RESTORE_PREFIX="${{ runner.os }}-pnpm-v${PNPM_VERSION}-store-${{ inputs.cache-prefix }}-"
echo "key=${CACHE_KEY}" >> "$GITHUB_OUTPUT"
echo "restore-prefix=${RESTORE_PREFIX}" >> "$GITHUB_OUTPUT"
echo "hash=${CACHE_HASH}" >> "$GITHUB_OUTPUT"
@@ -147,28 +107,16 @@ runs:
exit 1
fi
# 优先级:Docker 默认路径 > pnpm store path
STORE_DIR_CANDIDATE=""
# 1. 检查 Docker 环境默认路径
if [[ -d "/pnpm/store" ]]; then
STORE_DIR_CANDIDATE="/pnpm/store"
echo "📝 检测到 Docker 环境,使用默认路径: $STORE_DIR_CANDIDATE"
# 2. 通过 pnpm 命令获取
else
STORE_DIR_CANDIDATE=$(pnpm store path --silent 2>/dev/null | grep -v '^[[:space:]]*$' | tail -n1 | tr -d '\r\n')
if [[ -n "$STORE_DIR_CANDIDATE" ]]; then
echo "📝 通过 pnpm store path 获取: $STORE_DIR_CANDIDATE"
fi
fi
STORE_DIR_CANDIDATE=$(pnpm store path --silent 2>/dev/null || true)
STORE_DIR_CANDIDATE=$(echo "$STORE_DIR_CANDIDATE" | tail -n1 | tr -d '\r')
if [[ -z "$STORE_DIR_CANDIDATE" ]]; then
echo "❌ 无法确定 pnpm store 路径" >&2
echo "💡 提示: 确保 pnpm 配置正确或检查 /pnpm/store 目录" >&2
echo "❌ pnpm store path 未返回有效路径。可在运行前设置 PNPM_STORE_DIR=/path/to/store 或检查 pnpm 配置" >&2
exit 1
fi
echo "📦 pnpm store 路径: $STORE_DIR_CANDIDATE"
echo "pnpm store path: $STORE_DIR_CANDIDATE"
echo "PNPM_STORE_DIR=${STORE_DIR_CANDIDATE}" >> "$GITHUB_ENV"
echo "path=${STORE_DIR_CANDIDATE}" >> "$GITHUB_OUTPUT"
- name: 拉取缓存
@@ -184,94 +132,56 @@ runs:
shell: bash
run: |
if [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]]; then
echo "✅ 缓存完全命中"
echo "缓存命中"
else
echo "⚠️ 缓存未命中或部分命中,将从网络下载依赖"
echo "缓存未命中"
fi
- name: 安装依赖
shell: bash
run: |
set -euo pipefail
# 获取 store 路径(优先使用已配置的路径)
STORE_PATH="${{ steps.cache-path.outputs.path }}"
echo "📦 pnpm store 路径: $STORE_PATH"
# 记录安装前的 lockfile 状态
LOCKFILE_BEFORE=""
if [[ -f "pnpm-lock.yaml" ]]; then
LOCKFILE_BEFORE=$(md5sum pnpm-lock.yaml 2>/dev/null || md5 pnpm-lock.yaml 2>/dev/null || echo "")
fi
# 处理自定义安装命令
STORE_DIR="${PNPM_STORE_DIR:-${{ steps.cache-path.outputs.path }}}"
export PNPM_STORE_DIR="$STORE_DIR"
export npm_config_store_dir="$PNPM_STORE_DIR"
export PNPM_CONFIG_STORE_DIR="$PNPM_STORE_DIR"
echo "📦 Using PNPM_STORE_DIR=$PNPM_STORE_DIR"
if [[ -n "${{ inputs.install-command }}" ]]; then
echo "🔧 使用自定义安装命令: ${{ inputs.install-command }}"
eval "${{ inputs.install-command }}"
exit 0
fi
if [[ "${{ steps.cache.outputs.cache-hit }}" == "true" ]]; then
FLAGS=("--offline" "--frozen-lockfile")
else
# 构建安装参数
FLAGS=("--prefer-offline" "--frozen-lockfile")
if [[ "${{ inputs.force-install }}" == "true" ]]; then
FLAGS+=("--force")
echo "⚠️ 强制重新安装模式"
fi
INSTALL_ARGS="${{ inputs.install-args }}"
# 显示执行命令
if [[ -n "$INSTALL_ARGS" ]]; then
echo "🔧 执行: pnpm install ${FLAGS[*]} ${INSTALL_ARGS}"
pnpm install "${FLAGS[@]}" ${INSTALL_ARGS}
else
echo "🔧 执行: pnpm install ${FLAGS[*]}"
pnpm install "${FLAGS[@]}"
fi
fi
# 清理项目本地 .pnpm-store (如果存在且与全局 store 不同)
if [[ "${{ inputs.clean-project-store }}" == "true" && -d ".pnpm-store" ]]; then
GLOBAL_STORE=$(cd "$STORE_PATH" 2>/dev/null && pwd || echo "")
LOCAL_STORE=$(cd .pnpm-store 2>/dev/null && pwd || echo "")
if [[ -n "$GLOBAL_STORE" && -n "$LOCAL_STORE" && "$GLOBAL_STORE" != "$LOCAL_STORE" ]]; then
echo "🧹 清理项目本地 .pnpm-store 目录"
rm -rf .pnpm-store || true
fi
if [[ "${{ inputs.force-install }}" == "true" ]]; then
FLAGS+=("--force")
fi
# 严格检查 lockfile 是否被意外修改
if [[ "${{ inputs.strict-lockfile-check }}" == "true" && -n "$LOCKFILE_BEFORE" ]]; then
LOCKFILE_AFTER=""
if [[ -f "pnpm-lock.yaml" ]]; then
LOCKFILE_AFTER=$(md5sum pnpm-lock.yaml 2>/dev/null || md5 pnpm-lock.yaml 2>/dev/null || echo "")
fi
if [[ "$LOCKFILE_BEFORE" != "$LOCKFILE_AFTER" ]]; then
echo "" >&2
echo "❌ 检测到 pnpm-lock.yaml 在安装过程中被修改" >&2
echo "" >&2
echo "📋 变更内容:" >&2
git diff pnpm-lock.yaml || true
echo "" >&2
echo "💡 原因: package.json 与 pnpm-lock.yaml 不同步" >&2
echo "💡 解决: 在本地运行 'pnpm install' 并提交更新后的 lockfile" >&2
echo "💡 或者: 设置 strict-lockfile-check: 'false' 跳过此检查" >&2
echo "" >&2
exit 1
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" && "$(cd "$PNPM_STORE_DIR" 2>/dev/null && pwd)" != "$(cd .pnpm-store 2>/dev/null && pwd)" ]]; then
rm -rf .pnpm-store || true
fi
echo "🧾 git status --short"
CHANGES=$(git status --short || true)
if [[ -n "$CHANGES" ]]; then
echo "$CHANGES"
echo "❌ 安装依赖后检测到工作区存在未提交变更。请检查上述文件,必要时更新配置或在调用前设置 PNPM_STORE_DIR。" >&2
exit 1
else
echo "✅ 工作区保持干净"
fi
echo "✅ 依赖安装完成"
- name: 总结
shell: bash
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 pnpm 安装总结"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " pnpm 版本: $PNPM_VERSION"
echo " 缓存命中: ${{ steps.cache.outputs.cache-hit }}"
echo " 缓存 key: ${{ steps.cache-key.outputs.key }}"
echo " Store 路径: ${{ steps.cache-path.outputs.path }}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "pnpm版本: $PNPM_VERSION"
echo "缓存命中: ${{ steps.cache.outputs.cache-hit }}"
echo "缓存key: ${{ steps.cache-key.outputs.key }}"
-318
View File
@@ -1,318 +0,0 @@
# Setup OpenCode Action
使用 npm 全局安装 opencode-ai 并缓存,支持版本检测和自动更新。
## 功能特性
-**npm 安装**:使用 npm 全局安装,简单快速
- 🚀 **智能缓存**:缓存 npm 包和全局安装,加速后续构建
- 🔄 **自动更新**:版本变化时自动更新
- 🇨🇳 **国内镜像**:支持淘宝镜像源,国内环境友好
-**快速恢复**:缓存命中时跳过安装,秒级完成
## 使用方法
### 基础用法
```yaml
- name: 安装 OpenCode
uses: actions/xgj/setup-opencode@v1
```
### 国内环境使用(推荐)
```yaml
- name: 安装 OpenCode(淘宝镜像)
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
use-taobao-registry: 'true'
```
### 指定版本
```yaml
- name: 安装 OpenCode 1.2.3
uses: ./.gitea/actions/setup-opencode
with:
version: '1.2.3'
```
### 使用最新版本
```yaml
- name: 安装最新版 OpenCode
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
```
### 自定义 npm 包名
```yaml
- name: 安装自定义包
uses: actions/xgj/setup-opencode@v1
with:
package-name: 'opencode-ai' # 默认就是 opencode-ai,这里仅作演示
version: '1.2.3'
```
### 自定义 npm 镜像源
```yaml
- name: 使用自定义镜像源
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
npm-registry: 'https://registry.npmmirror.com'
```
### 强制重新安装
```yaml
- name: 强制重新安装 OpenCode
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
skip-cache: 'true'
```
### 使用输出
```yaml
- name: 安装 OpenCode
id: setup-opencode
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
- name: 显示版本信息
run: |
echo "安装版本: ${{ steps.setup-opencode.outputs.version }}"
echo "缓存命中: ${{ steps.setup-opencode.outputs.cache-hit }}"
echo "执行更新: ${{ steps.setup-opencode.outputs.updated }}"
- name: 使用 OpenCode
run: |
opencode --version
opencode build
```
## 输入参数
| 参数 | 描述 | 必填 | 默认值 |
| ---- | ---- | ---- | ------ |
| `version` | OpenCode 版本号(例如: 1.0.0, latest | 否 | `latest` |
| `package-name` | npm 包名 | 否 | `opencode-ai` |
| `use-taobao-registry` | 使用淘宝 npm 镜像源 (true/false) | 否 | `false` |
| `npm-registry` | 自定义 npm 镜像源地址 | 否 | `` |
| `cache-prefix` | 缓存前缀名称 | 否 | `opencode-npm` |
| `skip-cache` | 跳过缓存,强制重新安装 (true/false) | 否 | `false` |
## 输出
| 输出 | 描述 |
| ---- | ---- |
| `version` | 安装的 OpenCode 版本 |
| `cache-hit` | 缓存是否命中 (true/false) |
| `updated` | 是否执行了更新 (true/false) |
## 工作原理
1. **参数验证**:验证输入参数的有效性
2. **配置镜像源**:根据配置设置 npm 镜像源(支持淘宝镜像)
3. **缓存检查**:根据包名和版本生成缓存键,尝试恢复缓存
4. **版本比对**
- 检查已安装版本是否与请求版本一致
- 版本一致:跳过安装
- 版本不一致或未安装:执行 npm 安装
5. **npm 安装**:使用 `npm install -g` 全局安装指定版本
6. **保存缓存**:缓存 npm 包和全局安装目录
## 缓存策略
- **缓存内容**
- `~/.npm` - npm 缓存目录
- `/usr/local/lib/node_modules/{package}` - 全局安装的包
- `/usr/local/bin/{package}` - 全局命令
- **缓存键格式**`{OS}-{cache-prefix}-{package-name}-{version}`
- **缓存更新**:版本号变化时自动更新缓存
- **缓存生命周期**:遵循 GitHub Actions 缓存默认生命周期(最多 7 天未使用)
## 完整示例
### 示例 1:基础 CI/CD 流程
```yaml
name: Build with OpenCode
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
- name: 构建项目
run: |
opencode build --release
- name: 运行测试
run: |
opencode test
```
### 示例 2:国内环境使用
```yaml
name: Build in China
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 OpenCode(淘宝镜像)
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
use-taobao-registry: 'true'
- name: 构建
run: opencode build
```
### 示例 3:多版本测试
```yaml
name: Test Multiple Versions
on: [push]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
opencode-version: ['1.0.0', '1.1.0', '1.2.0', 'latest']
steps:
- uses: actions/checkout@v4
- name: 安装 OpenCode ${{ matrix.opencode-version }}
uses: actions/xgj/setup-opencode@v1
with:
version: ${{ matrix.opencode-version }}
- name: 运行测试
run: |
echo "Testing with OpenCode ${{ matrix.opencode-version }}"
opencode --version
opencode test
```
### 示例 4:带缓存状态检查
```yaml
name: Build with Cache Info
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 OpenCode
id: opencode
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
use-taobao-registry: 'true'
- name: 显示缓存状态
run: |
if [[ "${{ steps.opencode.outputs.cache-hit }}" == "true" ]]; then
echo "✅ 使用缓存,节省时间!"
else
echo "📥 首次安装或版本更新"
fi
if [[ "${{ steps.opencode.outputs.updated }}" == "true" ]]; then
echo "🔄 OpenCode 已更新到 ${{ steps.opencode.outputs.version }}"
fi
- name: 构建
run: opencode build
```
## 注意事项
1. **国内环境**:🇨🇳 在国内使用时,强烈建议设置 `use-taobao-registry: 'true'` 使用淘宝镜像源
2. **版本格式**:版本号应遵循语义化版本规范(如 1.2.3)
3. **npm 环境**:需要 Node.js 和 npm 已安装(GitHub Actions 默认已安装)
4. **全局安装**:使用 `npm install -g` 全局安装,命令自动添加到 PATH
5. **缓存限制**GitHub Actions 缓存有大小限制(10GB
## 故障排查
### 国内网络问题
```yaml
- name: 使用淘宝镜像源
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
use-taobao-registry: 'true'
```
### 自定义镜像源
```yaml
- name: 使用自定义镜像
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
npm-registry: 'https://registry.npmmirror.com'
```
### 安装失败
```yaml
- name: 清除缓存重新安装
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
skip-cache: 'true'
```
### 版本不匹配
检查 `updated` 输出,如果为 `true` 表示已更新到新版本。
## 优势
相比二进制安装方式,npm 安装具有以下优势:
- ✅ **简单直接**:一条 `npm install -g` 命令搞定
-**依赖管理**npm 自动处理依赖关系
-**版本管理**npm 原生支持版本管理
-**跨平台**npm 包通常支持多平台
-**更新方便**:版本更新只需修改版本号
-**缓存高效**:缓存 npm 目录和全局安装,恢复快速
## 许可证
与主仓库保持一致。
-288
View File
@@ -1,288 +0,0 @@
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-ai'
use-taobao-registry:
description: '使用淘宝 npm 镜像源 (true/false)'
required: false
default: 'false'
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 }}"
GLOBAL_ROOT="${{ steps.npm-paths.outputs.global-root }}"
NEED_INSTALL="true"
# 检查包目录是否存在(更可靠,不依赖命令名)
PACKAGE_DIR="${GLOBAL_ROOT}/${PACKAGE}"
if [[ -d "${PACKAGE_DIR}" ]]; then
# 从 package.json 获取版本
PACKAGE_JSON="${PACKAGE_DIR}/package.json"
if [[ -f "${PACKAGE_JSON}" ]]; then
INSTALLED_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "${PACKAGE_JSON}" | head -1 | cut -d'"' -f4 || echo "unknown")
else
INSTALLED_VERSION="unknown"
fi
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: 保存缓存
if: steps.check-installed.outputs.need-install == '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: |
# 直接从 opencode 命令获取版本
if command -v opencode &> /dev/null; then
VERSION=$(opencode --version 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
if [[ "${VERSION}" != "unknown" ]]; then
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "✅ OpenCode 版本: ${VERSION}"
else
# 如果命令输出格式不同,尝试直接输出
VERSION=$(opencode --version 2>/dev/null | head -n1 || echo "unknown")
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "✅ OpenCode 版本: ${VERSION}"
fi
else
# 备用方案:使用已解析的版本号
VERSION="${{ steps.resolve-version.outputs.version }}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "⚠️ opencode 命令不可用,使用已解析版本: ${VERSION}"
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 已准备就绪!"
-36
View File
@@ -1,36 +0,0 @@
name: Basic OpenCode Usage
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode
id: setup-opencode
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
- name: 显示安装信息
run: |
echo "OpenCode 版本: ${{ steps.setup-opencode.outputs.version }}"
echo "缓存命中: ${{ steps.setup-opencode.outputs.cache-hit }}"
echo "执行更新: ${{ steps.setup-opencode.outputs.updated }}"
- name: 验证安装
run: |
opencode --version
which opencode
- name: 使用 OpenCode
run: |
opencode build
-33
View File
@@ -1,33 +0,0 @@
name: China Mirror Example
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode(淘宝镜像)
id: setup
uses: actions/xgj/setup-opencode@v1
with:
version: 'latest'
use-taobao-registry: 'true'
- name: 显示安装信息
run: |
echo "安装版本: ${{ steps.setup.outputs.version }}"
echo "缓存命中: ${{ steps.setup.outputs.cache-hit }}"
if [[ "${{ steps.setup.outputs.cache-hit }}" == "true" ]]; then
echo "✅ 使用缓存,快速完成安装"
else
echo "📥 通过 npm 安装完成"
fi
- name: 构建项目
run: |
opencode build --release
@@ -1,21 +0,0 @@
name: Custom Registry Example
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 使用自定义 npm 镜像源
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
npm-registry: 'https://registry.npmmirror.com'
- name: 构建
run: |
opencode build
@@ -1,21 +0,0 @@
name: Specific Version Example
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 OpenCode 1.2.3
uses: actions/xgj/setup-opencode@v1
with:
version: '1.2.3'
cache-prefix: 'opencode-stable'
- name: 构建项目
run: |
opencode build --release