forgeplus/app/controllers/concerns/api/action_validator_helper.rb

165 lines
3.6 KiB
Ruby

require 'yaml'
require 'json'
module Api::ActionValidatorHelper
extend ActiveSupport::Concern
def validate_string(yaml_string)
begin
yaml_content = YAML.safe_load(yaml_string)
validate_content(yaml_content)
rescue Psych::SyntaxError => e
{ valid: false, errors: ["YAML语法错误: #{e.message}"] }
rescue => e
{ valid: false, errors: ["数据读取错误: #{e.message}"] }
end
end
def validate_content(data)
errors = []
warnings = []
# 基本结构验证
errors.concat(validate_basic_structure(data))
# 触发器验证
# errors.concat(validate_trigger(data))
# 任务验证
errors.concat(validate_job(data))
# 步骤验证
if data['jobs']
data['jobs'].each do |job_name, job_config|
errors.concat(validate_job_steps(job_name, job_config))
end
end
{
valid: errors.empty?,
errors: errors,
warnings: warnings,
job_count: data['jobs']&.size || 0
}
end
private
def validate_basic_structure(data)
errors = []
unless data.is_a?(Hash)
errors << "Action文件必须是YAML对象"
return errors
end
unless data.key?('name')
errors << "缺少必需字段: name"
end
unless data.key?(true)
errors << "缺少触发器配置: on"
end
unless data.key?('jobs')
errors << "缺少任务配置: jobs"
end
errors
end
def validate_trigger(data)
errors = []
if data[true]
if data[true]['push']
unless data[true]['push'].key?('branches')
errors << "push触发器缺少branches配置"
end
unless data[true]['push'].key?('paths-ignore')
errors << "push触发器缺少paths-ignore配置"
end
end
end
errors
end
def validate_job(data)
errors = []
if data['jobs']
jobs = data['jobs']
unless jobs.is_a?(Hash)
errors << "jobs字段必须是对象"
return errors
end
jobs.each do |job_name, job_config|
unless job_config.is_a?(Hash)
errors << "任务 #{job_name} 配置必须是对象"
next
end
# unless job_config.key?('name')
# errors << "任务 #{job_name} 缺少name字段"
# end
unless job_config.key?('runs-on')
errors << "任务 #{job_name} 缺少runs-on字段"
end
unless job_config.key?('steps')
errors << "任务 #{job_name} 缺少steps字段"
end
end
end
errors
end
def validate_job_steps(job_name, job_config)
errors = []
steps = job_config['steps'] || []
unless steps.is_a?(Array)
errors << "任务 #{job_name} 的steps必须是数组"
return errors
end
# 验证每个步骤的详细配置
steps.each_with_index do |step, index|
errors.concat(validate_step(job_name, step, index))
end
errors
end
def validate_step(job_name, step, index)
errors = []
unless step.is_a?(Hash)
errors << "任务 #{job_name} 步骤 #{index + 1} 必须是对象"
return errors
end
# unless step.key?('name')
# errors << "任务 #{job_name} 步骤 #{index + 1} 缺少name字段"
# end
# 验证uses或run字段
has_uses = step.key?('uses')
has_run = step.key?('run')
unless has_uses || has_run
errors << "任务 #{job_name} 步骤 #{index + 1} 必须包含uses或run字段"
end
if has_uses && has_run
errors << "任务 #{job_name} 步骤 #{index + 1} 不能同时包含uses和run字段"
end
errors
end
end