#!/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 [--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