forked from Trustie/forgeplus
165 lines
3.6 KiB
Ruby
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 |