Files
novalon-website/scripts/test-step.sh
T
张翔 6797a1ee2d
ci/woodpecker/push/woodpecker Pipeline failed
feat: 添加 Woodpecker CI 本地测试工具
- validate-woodpecker.sh: 全面验证配置文件
- test-step.sh: 单步测试工具,支持 Docker 隔离环境
- README.md: 详细使用文档和最佳实践

这些工具可以在本地验证和测试 CI/CD 配置,避免通过持续提交来测试
2026-03-28 22:24:46 +08:00

274 lines
6.3 KiB
Bash
Executable File
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.
#!/bin/bash
set -e
WOODPECKER_FILE=".woodpecker.yml"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_header() {
echo ""
echo -e "${BLUE}==========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}==========================================${NC}"
echo ""
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_info() {
echo -e "${BLUE}$1${NC}"
}
# 检查是否提供了步骤名称
if [ -z "$1" ]; then
print_header "Woodpecker CI 单步测试工具"
echo "用法: $0 <step_name> [--dry-run]"
echo ""
echo "示例:"
echo " $0 notify-wechat-success --dry-run # 仅显示命令,不执行"
echo " $0 lint # 执行 lint 步骤"
echo ""
echo "可用的步骤:"
python3 << 'PYTHON_SCRIPT'
import yaml
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
steps = config.get('steps', {})
for i, (step_name, step_config) in enumerate(steps.items(), 1):
image = step_config.get('image', 'N/A')
print(f" {i}. {step_name:<25} (镜像: {image})")
PYTHON_SCRIPT
exit 1
fi
STEP_NAME="$1"
DRY_RUN="${2:-}"
print_header "测试步骤: $STEP_NAME"
# 检查步骤是否存在
STEP_EXISTS=$(python3 << PYTHON_SCRIPT
import yaml
import sys
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
steps = config.get('steps', {})
step_name = "$STEP_NAME"
if step_name in steps:
print("yes")
else:
print("no")
PYTHON_SCRIPT
)
if [ "$STEP_EXISTS" != "yes" ]; then
print_error "步骤 '$STEP_NAME' 不存在"
exit 1
fi
print_success "步骤 '$STEP_NAME' 存在"
# 获取步骤配置
python3 << PYTHON_SCRIPT
import yaml
import json
import sys
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
step_name = "$STEP_NAME"
step_config = config['steps'][step_name]
print(f"\n📦 镜像: {step_config.get('image', 'N/A')}")
# 显示环境变量
env = step_config.get('environment', {})
if env:
print("\n🔐 环境变量:")
for key, value in env.items():
if isinstance(value, dict) and 'from_secret' in value:
print(f" - {key}: from_secret:{value['from_secret']}")
else:
print(f" - {key}: {value}")
# 显示 when 条件
when = step_config.get('when', {})
if when:
print("\n📋 执行条件:")
if 'branch' in when:
branches = when['branch']
if isinstance(branches, list):
print(f" - 分支: {', '.join(branches)}")
else:
print(f" - 分支: {branches}")
if 'event' in when:
events = when['event']
if isinstance(events, list):
print(f" - 事件: {', '.join(events)}")
else:
print(f" - 事件: {events}")
if 'status' in when:
statuses = when['status']
if isinstance(statuses, list):
print(f" - 状态: {', '.join(statuses)}")
else:
print(f" - 状态: {statuses}")
# 显示命令
commands = step_config.get('commands', [])
if commands:
print("\n📝 命令:")
for i, cmd in enumerate(commands, 1):
# 显示前100个字符
if len(cmd) > 100:
print(f" {i}. {cmd[:100]}...")
else:
print(f" {i}. {cmd}")
# 输出 JSON 配置(用于后续处理)
print("\n" + json.dumps(step_config))
PYTHON_SCRIPT
# 如果是 dry-run 模式,只显示信息,不执行
if [ "$DRY_RUN" = "--dry-run" ]; then
print_info "Dry-run 模式,不执行命令"
exit 0
fi
# 询问是否继续执行
echo ""
read -p "是否继续执行此步骤?(y/N) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "已取消执行"
exit 0
fi
# 执行步骤
print_header "执行步骤"
# 获取镜像
IMAGE=$(python3 << PYTHON_SCRIPT
import yaml
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
step_name = "$STEP_NAME"
print(config['steps'][step_name].get('image', ''))
PYTHON_SCRIPT
)
if [ -z "$IMAGE" ]; then
print_error "无法获取镜像信息"
exit 1
fi
print_info "使用镜像: $IMAGE"
# 拉取镜像
print_info "拉取镜像..."
docker pull "$IMAGE" || {
print_warning "镜像拉取失败,尝试使用本地镜像"
}
# 获取命令
COMMANDS=$(python3 << 'PYTHON_SCRIPT'
import yaml
import json
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
step_name = "$STEP_NAME"
commands = config['steps'][step_name].get('commands', [])
# 将命令转换为 JSON 字符串
print(json.dumps(commands))
PYTHON_SCRIPT
)
# 执行命令
print_info "执行命令..."
# 创建临时脚本
TEMP_SCRIPT=$(mktemp)
trap "rm -f $TEMP_SCRIPT" EXIT
# 写入命令
python3 << PYTHON_SCRIPT > "$TEMP_SCRIPT"
import yaml
import json
with open('.woodpecker.yml', 'r') as f:
config = yaml.safe_load(f)
step_name = "$STEP_NAME"
commands = config['steps'][step_name].get('commands', [])
for cmd in commands:
print(cmd)
PYTHON_SCRIPT
chmod +x "$TEMP_SCRIPT"
# 设置环境变量(模拟 Woodpecker CI
export CI="true"
export CI_COMMIT_BRANCH="${CI_COMMIT_BRANCH:-release/v1.0.0}"
export CI_COMMIT_SHA="${CI_COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo 'abc123def456')}"
export CI_COMMIT_MESSAGE="${CI_COMMIT_MESSAGE:-$(git log -1 --pretty=%B 2>/dev/null || echo 'Test commit')}"
export CI_COMMIT_AUTHOR="${CI_COMMIT_AUTHOR:-$(git log -1 --pretty=%an 2>/dev/null || echo 'Test Author')}"
export CI_PIPELINE_NUMBER="${CI_PIPELINE_NUMBER:-999}"
export CI_REPO_ID="${CI_REPO_ID:-1}"
print_info "环境变量:"
echo " CI_COMMIT_BRANCH: $CI_COMMIT_BRANCH"
echo " CI_COMMIT_SHA: $CI_COMMIT_SHA"
echo " CI_COMMIT_MESSAGE: $CI_COMMIT_MESSAGE"
echo " CI_COMMIT_AUTHOR: $CI_COMMIT_AUTHOR"
echo " CI_PIPELINE_NUMBER: $CI_PIPELINE_NUMBER"
echo ""
# 使用 Docker 执行
docker run --rm \
-v "$(pwd):/woodpecker/src" \
-w /woodpecker/src \
-e CI \
-e CI_COMMIT_BRANCH \
-e CI_COMMIT_SHA \
-e CI_COMMIT_MESSAGE \
-e CI_COMMIT_AUTHOR \
-e CI_PIPELINE_NUMBER \
-e CI_REPO_ID \
"$IMAGE" \
sh -c "$(cat "$TEMP_SCRIPT")"
if [ $? -eq 0 ]; then
print_success "步骤执行成功"
else
print_error "步骤执行失败"
exit 1
fi