Mike McQuaid ee253e465b Vendor all Homebrew's gems.
Homebrew's actually ended up using a fair few gems. While we want to
avoid Bundler at runtime (and this PR still does that, in fact uses
Bundler even less at runtime than it did before) writing our own version
to use at build-time seems redundant.
2017-05-07 13:52:57 +01:00

124 lines
4.1 KiB
Ruby

#: * `man` [`--fail-if-changed`]:
#: Generate Homebrew's manpages.
#:
#: If `--fail-if-changed` is passed, the command will return a failing
#: status code if changes are detected in the manpage outputs.
#: This can be used for CI to be notified when the manpages are out of date.
#: Additionally, the date used in new manpages will match those in the existing
#: manpages (to allow comparison without factoring in the date).
require "formula"
require "erb"
require "ostruct"
module Homebrew
module_function
SOURCE_PATH = HOMEBREW_LIBRARY_PATH/"manpages"
TARGET_MAN_PATH = HOMEBREW_REPOSITORY/"manpages"
TARGET_DOC_PATH = HOMEBREW_REPOSITORY/"docs"
def man
raise UsageError unless ARGV.named.empty?
if ARGV.flag? "--link"
odie "`brew man --link` is now done automatically by `brew update`."
end
regenerate_man_pages
if system "git", "-C", HOMEBREW_REPOSITORY, "diff", "--quiet", "docs/Manpage.md", "manpages"
puts "No changes to manpage output detected."
elsif ARGV.include?("--fail-if-changed")
Homebrew.failed = true
end
end
def regenerate_man_pages
Homebrew.run_bundler_if_needed!
markup = build_man_page
convert_man_page(markup, TARGET_DOC_PATH/"Manpage.md")
convert_man_page(markup, TARGET_MAN_PATH/"brew.1")
cask_markup = (SOURCE_PATH/"brew-cask.1.md").read
convert_man_page(cask_markup, TARGET_MAN_PATH/"brew-cask.1")
end
def path_glob_commands(glob)
Pathname.glob(glob)
.sort_by { |source_file| sort_key_for_path(source_file) }
.map do |source_file|
source_file.read.lines
.grep(/^#:/)
.map { |line| line.slice(2..-1) }
.join
end.reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") }
end
def build_man_page
template = (SOURCE_PATH/"brew.1.md.erb").read
variables = OpenStruct.new
variables[:commands] = path_glob_commands("#{HOMEBREW_LIBRARY_PATH}/cmd/*.{rb,sh}")
variables[:developer_commands] = path_glob_commands("#{HOMEBREW_LIBRARY_PATH}/dev-cmd/*.{rb,sh}")
readme = HOMEBREW_REPOSITORY/"README.md"
variables[:lead_maintainer] = readme.read[/(Homebrew's lead maintainer .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:maintainers] = readme.read[/(Homebrew's current maintainers .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:former_maintainers] = readme.read[/(Former maintainers .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
ERB.new(template, nil, ">").result(variables.instance_eval { binding })
end
def sort_key_for_path(path)
# Options after regular commands (`~` comes after `z` in ASCII table).
path.basename.to_s.sub(/\.(rb|sh)$/, "").sub(/^--/, "~~")
end
def convert_man_page(markup, target)
manual = target.basename(".1")
organisation = "Homebrew"
# Set the manpage date to the existing one if we're checking for changes.
# This avoids the only change being e.g. a new date.
date = if ARGV.include?("--fail-if-changed") &&
target.extname == ".1" && target.exist?
/"(\d{1,2})" "([A-Z][a-z]+) (\d{4})" "#{organisation}" "#{manual}"/ =~ target.read
Date.parse("#{$1} #{$2} #{$3}")
else
Date.today
end
date = date.strftime("%Y-%m-%d")
shared_args = %W[
--pipe
--organization=#{organisation}
--manual=#{target.basename(".1")}
--date=#{date}
]
format_flag, format_desc = target_path_to_format(target)
puts "Writing #{format_desc} to #{target}"
Utils.popen(["ronn", format_flag] + shared_args, "rb+") do |ronn|
ronn.write markup
ronn.close_write
ronn_output = ronn.read
ronn_output.gsub!(%r{</?var>}, "`") if format_flag == "--markdown"
target.atomic_write ronn_output
end
end
def target_path_to_format(target)
case target.basename
when /\.md$/ then ["--markdown", "markdown"]
when /\.\d$/ then ["--roff", "man page"]
else
odie "Failed to infer output format from '#{target.basename}'."
end
end
end