6797a1ee2d
ci/woodpecker/push/woodpecker Pipeline failed
- validate-woodpecker.sh: 全面验证配置文件 - test-step.sh: 单步测试工具,支持 Docker 隔离环境 - README.md: 详细使用文档和最佳实践 这些工具可以在本地验证和测试 CI/CD 配置,避免通过持续提交来测试
274 lines
6.3 KiB
Bash
Executable File
274 lines
6.3 KiB
Bash
Executable File
#!/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
|