mirror of
https://github.com/ivuorinen/homebrew-tap.git
synced 2026-02-11 00:48:40 +00:00
Initial Homebrew tap setup with automated documentation
- Add formula parser with Ruby AST parsing - Add GitHub Actions CI/CD workflows - Add Jekyll-based documentation site - Add RuboCop and Dependabot configuration - Add example formula for demonstration
This commit is contained in:
128
scripts/parse_formulas.rb
Executable file
128
scripts/parse_formulas.rb
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'date'
|
||||
|
||||
# Parser class for extracting metadata from Homebrew formulae
|
||||
class FormulaParser
|
||||
FORMULA_DIR = File.expand_path('../Formula', __dir__)
|
||||
OUTPUT_DIR = File.expand_path('../docs/_data', __dir__)
|
||||
OUTPUT_FILE = File.join(OUTPUT_DIR, 'formulae.json')
|
||||
|
||||
# Regex patterns for safe extraction without code evaluation
|
||||
PATTERNS = {
|
||||
class_name: /^class\s+(\w+)\s+<\s+Formula/,
|
||||
desc: /^\s*desc\s+["']([^"']+)["']/,
|
||||
homepage: /^\s*homepage\s+["']([^"']+)["']/,
|
||||
url: /^\s*url\s+["']([^"']+)["']/,
|
||||
version: /^\s*version\s+["']([^"']+)["']/,
|
||||
sha256: /^\s*sha256\s+["']([a-f0-9]{64})["']/i,
|
||||
license: /^\s*license\s+["']([^"']+)["']/,
|
||||
depends_on: /^\s*depends_on\s+["']([^"']+)["']/
|
||||
}.freeze
|
||||
|
||||
def self.run
|
||||
new.generate_documentation_data
|
||||
end
|
||||
|
||||
def generate_documentation_data
|
||||
ensure_output_directory
|
||||
formulae = parse_all_formulae
|
||||
write_json_output(formulae)
|
||||
puts "✅ Successfully generated documentation for #{formulae.length} formulae"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_output_directory
|
||||
FileUtils.mkdir_p(OUTPUT_DIR)
|
||||
end
|
||||
|
||||
def parse_all_formulae
|
||||
formula_files.map { |file| parse_formula(file) }.compact.sort_by { |f| f[:name] }
|
||||
end
|
||||
|
||||
def formula_files
|
||||
Dir.glob(File.join(FORMULA_DIR, '**', '*.rb'))
|
||||
end
|
||||
|
||||
def parse_formula(file_path)
|
||||
content = File.read(file_path)
|
||||
class_name = extract_value(content, :class_name)
|
||||
|
||||
return nil unless class_name
|
||||
|
||||
formula_name = convert_class_name_to_formula_name(class_name)
|
||||
|
||||
return nil if formula_name.nil? || formula_name.empty?
|
||||
|
||||
{
|
||||
name: formula_name,
|
||||
class_name: class_name,
|
||||
description: extract_value(content, :desc),
|
||||
homepage: extract_value(content, :homepage),
|
||||
url: extract_value(content, :url),
|
||||
version: extract_version(content),
|
||||
sha256: extract_value(content, :sha256),
|
||||
license: extract_value(content, :license),
|
||||
dependencies: extract_dependencies(content),
|
||||
file_path: Pathname.new(file_path).relative_path_from(Pathname.new(FORMULA_DIR)).to_s,
|
||||
last_modified: format_time_iso8601(File.mtime(file_path))
|
||||
}
|
||||
rescue StandardError => e
|
||||
warn "⚠️ Error parsing #{file_path}: #{e.message}"
|
||||
nil
|
||||
end
|
||||
|
||||
def extract_value(content, pattern_key)
|
||||
match = content.match(PATTERNS[pattern_key])
|
||||
match&.[](1)
|
||||
end
|
||||
|
||||
def extract_version(content)
|
||||
# Try explicit version first, then extract from URL
|
||||
explicit = extract_value(content, :version)
|
||||
return explicit if explicit
|
||||
|
||||
url = extract_value(content, :url)
|
||||
return nil unless url
|
||||
|
||||
# Common version patterns in URLs
|
||||
url.match(/v?(\d+(?:\.\d+)+)/)&.[](1)
|
||||
end
|
||||
|
||||
def extract_dependencies(content)
|
||||
content.scan(PATTERNS[:depends_on]).flatten.uniq
|
||||
end
|
||||
|
||||
def convert_class_name_to_formula_name(class_name)
|
||||
return nil unless class_name
|
||||
|
||||
# Convert CamelCase to kebab-case
|
||||
class_name
|
||||
.gsub(/([a-z\d])([A-Z])/, '\1-\2')
|
||||
.downcase
|
||||
end
|
||||
|
||||
def format_time_iso8601(time)
|
||||
# Format time manually for compatibility
|
||||
time.strftime('%Y-%m-%dT%H:%M:%S%z').gsub(/(\d{2})(\d{2})$/, '\1:\2')
|
||||
end
|
||||
|
||||
def write_json_output(formulae)
|
||||
output = {
|
||||
tap_name: 'ivuorinen/homebrew-tap',
|
||||
generated_at: format_time_iso8601(Time.now),
|
||||
formulae_count: formulae.length,
|
||||
formulae: formulae
|
||||
}
|
||||
|
||||
File.write(OUTPUT_FILE, JSON.pretty_generate(output))
|
||||
end
|
||||
end
|
||||
|
||||
# Run if executed directly
|
||||
FormulaParser.run if __FILE__ == $PROGRAM_NAME
|
||||
Reference in New Issue
Block a user