Module: Rails::Hyperdrive::BundlerArtifactDiscovery
- Defined in:
- lib/rails/hyperdrive/bundler_artifact_discovery.rb
Defined Under Namespace
Constant Summary collapse
- SKILL_FILE_NAMES =
["SKILL.md", "SKILL.md.erb"].freeze
Class Method Summary collapse
-
.discover(specs: nil, enabled_gems: [], report: Report.new) ⇒ Object
Non-fatal problems are reported and the artifact is dropped; discovery never raises.
-
.flat_keys(spec, kind, manifest:) ⇒ Object
Every filename a manifest may key one of the kind's artifacts by: the name as shipped, plus each template's rendered face.
-
.install_ready_body(artifact) ⇒ Object
Guideline frontmatter is stripped because the installed file is @-included eagerly into agent context, where it would be inert noise.
- .skill_paths(spec, manifest:, report: Report.new) ⇒ Object
- .split_frontmatter(body) ⇒ Object
-
.support_relpaths(root, glob: "**/*") ⇒ Object
Dir-relative paths of everything under
rootbesides SKILL.md(.erb). - .target_path(path) ⇒ Object
-
.template_dir_for(path, support_root) ⇒ Object
nil when the definition file sits in the support root, i.e.
Class Method Details
.discover(specs: nil, enabled_gems: [], report: Report.new) ⇒ Object
Non-fatal problems are reported and the artifact is dropped; discovery
never raises. A gem that has not opted in as a companion is never
scanned — its skills.sh content is only reported through notices.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 78 def discover(specs: nil, enabled_gems: [], report: Report.new) specs ||= safe_bundler_specs enabled = Array(enabled_gems).map(&:to_s) resolved = specs.each_with_object({}) { |s, h| h[s.name.to_s] = s.version } candidates = [] specs.each do |spec| report.bundled_gems << spec.name.to_s unless opted_in?(spec, enabled_gems: enabled) notice_skills_sh_content(spec, report: report) next end manifest = GemManifest.load(spec, warnings: report.warnings) seen = each_artifact_path(spec, manifest: manifest, report: report) do |path, kind, support_root, key| gate = manifest.gate(kind.type, key) artifact = parse(path, source_spec: spec, kind: kind, resolved: resolved, report: report, support_root: support_root, gate: gate, key: key, manifest: manifest) if artifact.is_a?(Artifact) candidates << artifact elsif artifact != :gate_miss report.mark_skipped_gem(spec.name.to_s) end end warn_unknown_manifest_keys(manifest, spec, seen, report) end # Collapse same-name variants within one source gem (highest # spec_version, path as tiebreak); never across sources — composite # identity is (name, source_gem). candidates.group_by { |a| [a.name, a.source_gem, a.artifact_type] }.map do |_key, group| group.max_by { |a| [Gem::Version.new(a.spec_version), a.path] } end end |
.flat_keys(spec, kind, manifest:) ⇒ Object
Every filename a manifest may key one of the kind's artifacts by: the name as shipped, plus each template's rendered face. Templates the tie rule drops are included — the manifest names shipped files, not surviving ones.
230 231 232 233 234 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 230 def flat_keys(spec, kind, manifest:) flat_candidates(spec, kind, manifest: manifest) .flat_map { |path| [File.basename(path), File.basename(target_path(path))] } .uniq end |
.install_ready_body(artifact) ⇒ Object
Guideline frontmatter is stripped because the installed file is @-included eagerly into agent context, where it would be inert noise.
595 596 597 598 599 600 601 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 595 def install_ready_body(artifact) kind = InstallLayout.kind(artifact.artifact_type) return artifact.body unless kind&.install_body == :strip_frontmatter _frontmatter, rest = split_frontmatter(artifact.body) (rest || artifact.body).sub(/\A\n+/, "") end |
.skill_paths(spec, manifest:, report: Report.new) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 145 def skill_paths(spec, manifest:, report: Report.new) roots = artifact_roots(spec, InstallLayout.kind(:skill), manifest) candidates = [] seen = {} roots.each do |root| Dir.glob(File.join(root, "**", "{SKILL.md,SKILL.md.erb}")) .group_by { |p| File.dirname(p) }.each do |dir, group| next if seen[File.(dir)] seen[File.(dir)] = true candidates << { dir: dir, rel: dir.delete_prefix(root).delete_prefix("/"), path: resolve_same_dir_tie(dir, group, report) } end end pair_with_templates(candidates, spec, manifest: manifest, report: report) end |
.split_frontmatter(body) ⇒ Object
603 604 605 606 607 608 609 610 611 612 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 603 def split_frontmatter(body) lines = body.lines return [nil, body] unless lines.first&.strip == "---" closing_index = lines[1..].index { |l| l.strip == "---" } return [nil, body] unless closing_index absolute_closing = closing_index + 1 [lines[1...absolute_closing].join, lines[(absolute_closing + 1)..].join] end |
.support_relpaths(root, glob: "**/*") ⇒ Object
Dir-relative paths of everything under root besides SKILL.md(.erb).
".." segments are rejected to keep the tree inside that directory.
414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 414 def support_relpaths(root, glob: "**/*") return [] if root.nil? Dir.glob(File.join(root, glob)).filter_map do |file| next unless File.file?(file) rel = file.delete_prefix("#{root}/") next if SKILL_FILE_NAMES.include?(File.basename(rel)) next if rel.split(%r{[/\\]}).include?("..") rel end end |
.target_path(path) ⇒ Object
589 590 591 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 589 def target_path(path) erb_template?(path) ? path.delete_suffix(".erb") : path end |
.template_dir_for(path, support_root) ⇒ Object
nil when the definition file sits in the support root, i.e. the skill is not template-paired.
428 429 430 431 |
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 428 def template_dir_for(path, support_root) dir = File.dirname(path) dir unless File.(dir) == File.(support_root) end |