require "hoe/rake"
##
# Publish plugin for hoe.
#
# === Tasks Provided:
#
# announce:: Create news email file and optionally publish docs.
# debug_email:: Generate email announcement file.
# post_blog:: Post announcement to blog.
# publish_docs:: Publish RDoc to `rdoc_locations`.
# ridocs:: Generate ri locally for testing.
#
# === Extra Configuration Options:
#
# publish_on_announce:: Run +publish_docs+ when you run +release+.
# blogs:: An array of hashes of blog settings.
#
# The blogs entry can either look like:
#
# - path: ~/Work/p4/zss/www/blog.zenspider.com/releases
# type: zenweb
# cmd: rake sync (optional)
#
# or:
#
# - url: http://example.com/cgi-bin/blog.cgi
# blog_id: 1
# user: username
# password: passwd
# extra_headers:
# blah: whatever
module Hoe::Publish
##
# Optional: An array of the project's blog categories. Defaults to project
# name.
attr_accessor :blog_categories
##
# Optional: Name of destination directory for RDoc generated files.
# [default: doc]
attr_accessor :local_rdoc_dir
##
# Optional: Should RDoc and ri generation tasks be defined? [default: true]
#
# Allows you to define custom RDoc tasks then use the publish_rdoc task to
# upload them all. See also local_rdoc_dir
attr_accessor :need_rdoc
##
# Optional: An array of remote (rsync) paths to copy rdoc to.
#
# eg:
#
# rdoc_locations << "user@server:Sites/rdoc/#{remote_rdoc_dir}"
attr_accessor :rdoc_locations
##
# Optional: Name of RDoc destination directory. [default: +name+]
attr_accessor :remote_rdoc_dir
##
# Optional: Flags for RDoc rsync. [default: "-av --delete"]
attr_accessor :rsync_args
Hoe::DEFAULT_CONFIG["publish_on_announce"] = true
Hoe::DEFAULT_CONFIG["blogs"] = [
{
"user" => "user",
"password" => "password",
"url" => "url",
"blog_id" => "blog_id",
"extra_headers" => {
"mt_convert_breaks" => "markdown",
},
},
]
##
# Initialize variables for plugin.
def initialize_publish
self.blog_categories ||= [self.name]
self.local_rdoc_dir ||= "doc"
self.need_rdoc ||= true
self.rdoc_locations ||= []
self.remote_rdoc_dir ||= self.name
self.rsync_args ||= "-av -O --delete"
end
##
# Declare a dependency on rdoc, IF NEEDED.
def activate_publish_deps
dependency "rdoc", [">= 4.0", "< 7"], :developer if need_rdoc
end
##
# Define tasks for plugin.
def define_publish_tasks
if need_rdoc then
task :isolate # ensure it exists
desc "Generate rdoc"
task :docs => [:clobber_docs, :isolate] do
sh(*make_rdoc_cmd)
end
desc "Generate rdoc coverage report"
task :dcov => :isolate do
sh(*make_rdoc_cmd("-C"))
end
desc "Remove RDoc files"
task :clobber_docs do
rm_rf local_rdoc_dir
end
task :clobber => :clobber_docs
desc "Generate ri locally for testing."
task :ridocs => [:clean, :isolate] do
sh(*make_rdoc_cmd("--ri", "-o", "ri"))
end
end
desc "Publish RDoc to wherever you want."
task :publish_docs => [:clean, :docs] do
publish_docs_task
end
# no doco for this one
task :publish_on_announce do
publish_on_announce_task
end
desc "Generate email announcement file."
task :debug_email do
puts generate_email ENV["FULL"]
end
desc 'Post announcement to blog. Uses the "blogs" array in your hoerc.'
task :post_blog do
post_blog_task
end
desc "Announce your release."
task :announce => [:post_blog, :publish_on_announce ]
end
def publish_docs_task # :nodoc:
warn "no rdoc_location values" if rdoc_locations.empty?
self.rdoc_locations.each do |dest|
sh %(rsync #{rsync_args} #{local_rdoc_dir}/ #{dest})
end
end
def publish_on_announce_task # :nodoc:
with_config do |config, _|
Rake::Task["publish_docs"].invoke if config["publish_on_announce"]
end
end
def post_blog_task # :nodoc:
with_config do |config, _path|
break unless config["blogs"]
config["blogs"].each do |site|
if site["path"] then
msg = "post_blog_#{site["type"]}"
send msg, site
system site["cmd"] if site["cmd"]
else
require "xmlrpc/client"
_, title, body, urls = announcement
body += "\n\n#{urls}"
server = XMLRPC::Client.new2(site["url"])
content = site["extra_headers"].merge(:title => title,
:description => body,
:categories => blog_categories)
server.call("metaWeblog.newPost",
site["blog_id"],
site["user"],
site["password"],
content,
true)
end
end
end
end
def make_rdoc_cmd(*extra_args) # :nodoc:
title = "#{name}-#{version} Documentation"
title = "#{group_name}'s #{title}" if group_name != name
(
%W[#{Gem.ruby} -S rdoc
--title #{title}
-o #{local_rdoc_dir}
] +
spec.rdoc_options +
extra_args +
spec.require_paths +
spec.extra_rdoc_files
).reject(&:empty?)
end
def post_blog_zenweb site # :nodoc:
dir = site["path"]
_, title, body, urls = announcement
body += "\n\n#{urls}"
Dir.chdir File.expand_path dir do
time = Time.at Time.now.to_i # nukes fractions
path = [time.strftime("%Y-%m-%d-"),
title.sub(/\W+$/, "").gsub(/\W+/, "-"),
".html.md"].join
header = {
"title" => title,
"categories" => blog_categories,
"date" => time,
}
File.open path, "w" do |f|
f.puts header.to_yaml.gsub(/\s$/, "")
f.puts "..."
f.puts
f.puts body
end
end
end
def generate_email full = nil # :nodoc:
require "time"
abort "No email 'to' entry. Run `rake config_hoe` to fix." unless
!full || email_to
from_name, from_email = author.first, email.first
subject, title, body, urls = announcement
[
full && "From: #{from_name} <#{from_email}>",
full && "To: #{email_to.join(", ")}",
full && "Date: #{Time.now.rfc2822}",
"Subject: [ANN] #{subject}",
"",
title,
"",
urls,
"",
body,
].compact.join("\n")
end
def announcement # :nodoc:
changes = self.changes.rdoc_to_markdown
subject = "#{name} #{version} Released"
title = "#{name} version #{version} has been released!"
body = "#{description}\n\nChanges:\n\n#{changes}".rdoc_to_markdown
urls =
case self.urls
when Hash then
self.urls.map { |k, v| "* #{k}: <#{v.strip.rdoc_to_markdown}>" }
when Array then
self.urls.map { |s| "* <#{s.strip.rdoc_to_markdown}>" }
else
raise "unknown urls format: #{urls.inspect}"
end
return subject, title, body, urls.join("\n")
end
end
class ::Rake::SshDirPublisher # :nodoc:
attr_reader :host, :remote_dir, :local_dir
end
class String # :nodoc:
##
# Very basic munge from rdoc to markdown format.
def rdoc_to_markdown
self.gsub(/^mailto:/, "").gsub(/^(=+)/) { "#" * $1.size }
end
end