244 lines
8.0 KiB
Ruby
Raw Normal View History

#: * `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"
2018-03-24 21:26:16 +05:30
require "cli_parser"
require "dev-cmd/audit"
2018-09-28 21:39:52 +05:30
require "dev-cmd/bottle"
require "dev-cmd/bump-formula-pr"
require "dev-cmd/create"
require "dev-cmd/edit"
require "dev-cmd/extract"
2018-09-28 21:39:52 +05:30
require "dev-cmd/formula"
require "dev-cmd/irb"
require "dev-cmd/linkage"
require "dev-cmd/mirror"
require "dev-cmd/prof"
2018-09-28 21:39:52 +05:30
require "dev-cmd/pull"
require "dev-cmd/release-notes"
require "dev-cmd/ruby"
2018-09-28 21:39:52 +05:30
require "dev-cmd/tap-new"
require "dev-cmd/test"
2018-09-28 21:39:52 +05:30
require "dev-cmd/tests"
require "dev-cmd/update-test"
module Homebrew
2016-09-26 01:44:51 +02:00
module_function
SOURCE_PATH = HOMEBREW_LIBRARY_PATH/"manpages"
2016-09-19 19:58:39 +01:00
TARGET_MAN_PATH = HOMEBREW_REPOSITORY/"manpages"
TARGET_DOC_PATH = HOMEBREW_REPOSITORY/"docs"
def man_args
Homebrew::CLI::Parser.new do
2018-09-28 21:39:52 +05:30
usage_banner <<~EOS
`man` [<options>]
2018-09-28 21:39:52 +05:30
Generate Homebrew's manpages.
EOS
switch "--fail-if-changed",
description: "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)."
switch "--link",
description: "This is now done automatically by `brew update`."
end
end
def man
man_args.parse
2018-03-24 21:26:16 +05:30
raise UsageError unless ARGV.named.empty?
if args.link?
2016-08-24 10:08:54 +01:00
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 args.fail_if_changed?
Homebrew.failed = true
end
end
def regenerate_man_pages
Homebrew.install_bundler_gems!
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
2016-09-05 21:46:40 +01:00
def path_glob_commands(glob)
2016-09-11 17:41:51 +01:00
Pathname.glob(glob)
.sort_by { |source_file| sort_key_for_path(source_file) }
.map(&:read).map(&:lines)
.map { |lines| lines.grep(/^#:/).map { |line| line.slice(2..-1) }.join }
.reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") }
2016-09-05 21:46:40 +01:00
end
def build_man_page
template = (SOURCE_PATH/"brew.1.md.erb").read
variables = OpenStruct.new
2016-09-05 21:46:40 +01:00
variables[:commands] = path_glob_commands("#{HOMEBREW_LIBRARY_PATH}/cmd/*.{rb,sh}")
2018-09-28 21:39:52 +05:30
variables[:developer_commands] = generate_cmd_manpages("#{HOMEBREW_LIBRARY_PATH}/dev-cmd/*.{rb,sh}")
variables[:global_options] = global_options_manpage_lines
readme = HOMEBREW_REPOSITORY/"README.md"
variables[:lead_maintainer] =
readme.read[/(Homebrew's lead maintainer .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:leadership] =
readme.read[/(Homebrew's project leadership committee .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:brew_maintainers] =
readme.read[%r{(Homebrew/brew's other current maintainers .*\.)}, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:linux_maintainers] =
readme.read[%r{(Homebrew/brew's Linux support \(and Linuxbrew\) maintainers are .*\.)}, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:core_maintainers] =
readme.read[%r{(Homebrew/homebrew-core's other current maintainers .*\.)}, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
variables[:former_maintainers] =
readme.read[/(Former maintainers .*\.)/, 1]
.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
2016-06-14 21:01:50 +08:00
variables[:homebrew_bundle] = help_output(:bundle)
variables[:homebrew_services] = help_output(:services)
2016-09-11 17:41:51 +01:00
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 args.fail_if_changed? &&
target.extname == ".1" && target.exist?
/"(\d{1,2})" "([A-Z][a-z]+) (\d{4})" "#{organisation}" "#{manual}"/ =~ target.read
Date.parse("#{Regexp.last_match(1)} #{Regexp.last_match(2)} #{Regexp.last_match(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
odie "Got no output from ronn!" unless ronn_output
if format_flag == "--markdown"
ronn_output = ronn_output.gsub(%r{<var>(.*?)</var>}, "*`\\1`*")
elsif format_flag == "--roff"
2018-10-03 10:19:46 +05:30
ronn_output = ronn_output.gsub(%r{<code>(.*?)</code>}, "\\fB\\1\\fR")
.gsub(%r{<var>(.*?)</var>}, "\\fI\\1\\fR")
.gsub(/(^\[?\\fB.+): /, "\\1\n ")
2018-10-02 19:54:22 +05:30
end
target.atomic_write ronn_output
end
end
def help_output(command)
tap = Tap.fetch("Homebrew/homebrew-#{command}")
tap.install unless tap.installed?
command_help_lines(which("brew-#{command}.rb", Tap.cmd_directories))
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
2018-09-08 22:21:04 +05:30
2018-09-28 21:39:52 +05:30
def generate_cmd_manpages(glob)
cmd_paths = Pathname.glob(glob).sort
2018-09-28 21:39:52 +05:30
man_page_lines = []
man_args = Homebrew.args
2018-09-28 21:39:52 +05:30
cmd_paths.each do |cmd_path|
begin
cmd_parser = Homebrew.send(cmd_arg_parser(cmd_path))
man_page_lines << cmd_manpage_lines(cmd_parser).join
2018-09-28 21:39:52 +05:30
rescue NoMethodError
2018-10-03 10:19:46 +05:30
man_page_lines << path_glob_commands(cmd_path.to_s).first
2018-09-28 21:39:52 +05:30
end
end
Homebrew.args = man_args
2018-09-28 21:39:52 +05:30
man_page_lines
end
def cmd_arg_parser(cmd_path)
2018-10-02 19:54:22 +05:30
"#{cmd_path.basename.to_s.gsub(".rb", "").tr("-", "_")}_args".to_sym
2018-09-28 21:39:52 +05:30
end
def cmd_manpage_lines(cmd_parser)
2018-10-02 19:54:22 +05:30
lines = [format_usage_banner(cmd_parser.usage_banner_text)]
2018-09-08 22:21:04 +05:30
lines += cmd_parser.processed_options.map do |short, long, _, desc|
next if !long.nil? && cmd_parser.global_option?(cmd_parser.option_to_name(long))
generate_option_doc(short, long, desc)
end
lines
end
def global_options_manpage_lines
lines = ["These options are applicable across all sub-commands.\n"]
lines += Homebrew::CLI::Parser.global_options.values.map do |names, _, desc|
short, long = names
2018-09-08 22:21:04 +05:30
generate_option_doc(short, long, desc)
end
lines
end
def generate_option_doc(short, long, desc)
comma = (short && long) ? ", " : ""
"* #{format_short_opt(short)}" + comma + "#{format_long_opt(long)}:" + "\n " + desc + "\n"
2018-09-08 22:21:04 +05:30
end
def format_short_opt(opt)
"`#{opt}`" unless opt.nil?
2018-09-08 22:21:04 +05:30
end
def format_long_opt(opt)
"`#{opt}`" unless opt.nil?
2018-09-08 22:21:04 +05:30
end
def format_usage_banner(usage_banner)
usage_banner.sub(/^/, "### ")
end
end