Guest User

Untitled

a guest
Feb 20th, 2018
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.77 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2.  
  3. require "optparse"
  4. require "ostruct"
  5.  
  6. SENDMAIL = "/usr/sbin/sendmail"
  7.  
  8. def parse(args)
  9. options = OpenStruct.new
  10. options.to = []
  11. options.error_to = []
  12. options.from = nil
  13. options.repository_uri = nil
  14. options.rss_path = nil
  15. options.rss_uri = nil
  16. options.name = nil
  17. options.viewvc_uri = nil
  18.  
  19. opts = OptionParser.new do |opts|
  20. opts.separator ""
  21.  
  22. opts.on("-I", "--include [PATH]",
  23. "Add [PATH] to load path") do |path|
  24. $LOAD_PATH.unshift(path)
  25. end
  26.  
  27. opts.on("-t", "--to [TO]",
  28. "Add [TO] to to address") do |to|
  29. options.to << to unless to.nil?
  30. end
  31.  
  32. opts.on("-e", "--error-to [TO]",
  33. "Add [TO] to to address when error is occurred") do |to|
  34. options.error_to << to unless to.nil?
  35. end
  36.  
  37. opts.on("-f", "--from [FROM]",
  38. "Use [FROM] as from address") do |from|
  39. options.from = from
  40. end
  41.  
  42. opts.on("--viewvc-uri [URI]",
  43. "Use [URI] as URI of viewvc") do |uri|
  44. options.viewvc_uri = uri
  45. end
  46.  
  47. opts.on("-r", "--repository-uri [URI]",
  48. "Use [URI] as URI of repository") do |uri|
  49. options.repository_uri = uri
  50. end
  51.  
  52. opts.on("--rss-path [PATH]",
  53. "Use [PATH] as output RSS path") do |path|
  54. options.rss_path = path
  55. end
  56.  
  57. opts.on("--rss-uri [URI]",
  58. "Use [URI] as output RSS URI") do |uri|
  59. options.rss_uri = uri
  60. end
  61.  
  62. opts.on("--name [NAME]",
  63. "Use [NAME] as repository name") do |name|
  64. options.name = name
  65. end
  66.  
  67. opts.on_tail("--help", "Show this message") do
  68. puts opts
  69. exit
  70. end
  71. end
  72.  
  73. opts.parse!(args)
  74.  
  75. options
  76. end
  77.  
  78. def make_body(info, params)
  79. body = ""
  80. body << "#{info.author}\t#{format_time(info.date)}\n"
  81. body << "\n"
  82. body << " New Revision: #{info.revision}\n"
  83. body << "\n"
  84. body << change_info(info, params[:viewvc_uri])
  85. body << "\n"
  86. body << " Log:\n"
  87. body << info.log.gsub(/^\t*/, " ").rstrip
  88. body << "\n\n"
  89. body << added_dirs(info)
  90. body << added_files(info)
  91. body << deleted_dirs(info)
  92. body << deleted_files(info)
  93. body << modified_dirs(info)
  94. body << modified_files(info)
  95. body.rstrip + "\n"
  96. end
  97.  
  98. def format_time(time)
  99. time.strftime('%Y-%m-%d %X %z (%a, %d %b %Y)')
  100. end
  101.  
  102. def changed_items(title, type, items)
  103. rv = ""
  104. unless items.empty?
  105. rv << " #{title} #{type}:\n"
  106. rv << items.collect {|item| " #{item}\n"}.join('')
  107. end
  108. rv
  109. end
  110.  
  111. def changed_files(title, files)
  112. changed_items(title, "files", files)
  113. end
  114.  
  115. def added_files(info)
  116. changed_files("Added", info.added_files)
  117. end
  118.  
  119. def deleted_files(info)
  120. changed_files("Removed", info.deleted_files)
  121. end
  122.  
  123. def modified_files(info)
  124. changed_files("Modified", info.updated_files)
  125. end
  126.  
  127. def changed_dirs(title, files)
  128. changed_items(title, "directories", files)
  129. end
  130.  
  131. def added_dirs(info)
  132. changed_dirs("Added", info.added_dirs)
  133. end
  134.  
  135. def deleted_dirs(info)
  136. changed_dirs("Removed", info.deleted_dirs)
  137. end
  138.  
  139. def modified_dirs(info)
  140. changed_dirs("Modified", info.updated_dirs)
  141. end
  142.  
  143.  
  144. CHANGED_TYPE = {
  145. :added => "Added",
  146. :modified => "Modified",
  147. :deleted => "Deleted",
  148. :copied => "Copied",
  149. :property_changed => "Property changed",
  150. }
  151.  
  152. def change_info(info, uri)
  153. " #{uri}?view=rev&revision=#{info.revision}\n"
  154. end
  155.  
  156. def changed_dirs_info(info, uri)
  157. rev = info.revision
  158. (info.added_dirs.collect do |dir|
  159. " Added: #{dir}\n"
  160. end + info.deleted_dirs.collect do |dir|
  161. " Deleted: #{dir}\n"
  162. end + info.updated_dirs.collect do |dir|
  163. " Modified: #{dir}\n"
  164. end).join("\n")
  165. end
  166.  
  167. def diff_info(info, uri)
  168. info.diffs.collect do |key, values|
  169. [
  170. key,
  171. values.collect do |type, value|
  172. case type
  173. when :added
  174. command = "cat"
  175. rev = "?revision=#{info.revision}&view=markup"
  176. when :modified, :property_changed
  177. command = "diff"
  178. rev = "?r1=#{info.revision}&r2=#{info.revision - 1}&diff_format=u"
  179. when :deleted, :copied
  180. command = "cat"
  181. rev = ""
  182. else
  183. raise "unknown diff type: #{value[:type]}"
  184. end
  185.  
  186. link = [uri, key.sub(/ .+/,"")||""].join("/") + rev
  187.  
  188. =begin without_diff
  189. desc = <<-HEADER
  190. #{CHANGED_TYPE[value[:type]]}: #{key} (+#{value[:added]} -#{value[:deleted]})
  191. HEADER
  192.  
  193. # result << <<-CONTENT
  194. # % svn #{command} -r #{rev} #{link}
  195. # CONTENT
  196.  
  197. desc << value[:body]
  198. =end
  199. desc = ''
  200.  
  201. [desc, link]
  202. end
  203. ]
  204. end
  205. end
  206.  
  207. def make_header(to, from, info, params)
  208. headers = []
  209. headers << x_author(info)
  210. headers << x_repository(info)
  211. headers << x_revision(info)
  212. headers << x_id(info)
  213. # headers << x_sha256(info)
  214. headers << "Content-Type: text/plain; charset=us-ascii"
  215. headers << "Content-Transfer-Encoding: 7bit"
  216. headers << "From: #{from}"
  217. headers << "To: #{to.join(' ')}"
  218. headers << "Subject: #{make_subject(params[:name], info)}"
  219. headers.find_all do |header|
  220. /\A\s*\z/ !~ header
  221. end.join("\n")
  222. end
  223.  
  224. def make_subject(name, info)
  225. branches = info.sha256.map{|x,| x[/(?:branches\/|tags\/)?(.+?)\//, 1]}.uniq
  226. subject = ""
  227. subject << "#{name}:" if name
  228. subject << "r#{info.revision}"
  229. subject << " (#{branches.join(', ')})" unless branches.empty?
  230. subject << ": "
  231. subject << info.log.to_a.first.to_s.strip
  232. subject
  233. end
  234.  
  235. def x_author(info)
  236. "X-SVN-Author: #{info.author}"
  237. end
  238.  
  239. def x_repository(info)
  240. # "X-SVN-Repository: #{info.path}"
  241. "X-SVN-Repository: XXX"
  242. end
  243.  
  244. def x_id(info)
  245. "X-SVN-Commit-Id: #{info.entire_sha256}"
  246. end
  247.  
  248. def x_revision(info)
  249. "X-SVN-Revision: #{info.revision}"
  250. end
  251.  
  252. def x_sha256(info)
  253. info.sha256.collect do |name, inf|
  254. "X-SVN-SHA256-Info: #{name}, #{inf[:revision]}, #{inf[:sha256]}"
  255. end.join("\n")
  256. end
  257.  
  258. def make_mail(to, from, info, params)
  259. make_header(to, from, info, params) + "\n" + make_body(info, params)
  260. end
  261.  
  262. def sendmail(to, from, mail)
  263. open("| #{SENDMAIL} #{to.join(' ')}", "w") do |f|
  264. f.print(mail)
  265. end
  266. end
  267.  
  268. def output_rss(name, file, rss_uri, repos_uri, info)
  269. prev_rss = nil
  270. begin
  271. if File.exist?(file)
  272. File.open(file) do |f|
  273. prev_rss = RSS::Parser.parse(f)
  274. end
  275. end
  276. rescue RSS::Error
  277. end
  278.  
  279. File.open(file, "w") do |f|
  280. f.print(make_rss(prev_rss, name, rss_uri, repos_uri, info).to_s)
  281. end
  282. end
  283.  
  284. def make_rss(base_rss, name, rss_uri, repos_uri, info)
  285. RSS::Maker.make("1.0") do |maker|
  286. maker.encoding = "UTF-8"
  287.  
  288. maker.channel.about = rss_uri
  289. maker.channel.title = rss_title(name || repos_uri)
  290. maker.channel.link = repos_uri
  291. maker.channel.description = rss_title(name || repos_uri)
  292. maker.channel.dc_date = info.date
  293.  
  294. if base_rss
  295. base_rss.items.each do |item|
  296. item.setup_maker(maker)
  297. end
  298. end
  299.  
  300. diff_info(info, repos_uri).each do |name, infos|
  301. infos.each do |desc, link|
  302. item = maker.items.new_item
  303. item.title = name
  304. item.description = desc
  305. item.content_encoded = "<pre>#{h(desc)}</pre>"
  306. item.link = link
  307. item.dc_date = info.date
  308. item.dc_creator = info.author
  309. end
  310. end
  311.  
  312. maker.items.do_sort = true
  313. maker.items.max_size = 15
  314. end
  315. end
  316.  
  317. def rss_title(name)
  318. "Repository of #{name}"
  319. end
  320.  
  321. def rss_items(items, info, repos_uri)
  322. diff_info(info, repos_uri).each do |name, infos|
  323. infos.each do |desc, link|
  324. items << [link, name, desc, info.date]
  325. end
  326. end
  327.  
  328. items.sort_by do |uri, title, desc, date|
  329. date
  330. end.reverse
  331. end
  332.  
  333. def main
  334. repos, revision, to, *rest = ARGV
  335. options = parse(rest)
  336.  
  337. require "svn/info"
  338. info = Svn::Info.new(repos, revision)
  339. from = options.from || info.author + "@ruby-lang.org"
  340. to = [to, *options.to]
  341. params = {
  342. :repository_uri => options.repository_uri,
  343. :viewvc_uri => options.viewvc_uri,
  344. :name => options.name
  345. }
  346. sendmail(to, from, make_mail(to, from, info, params))
  347.  
  348. if options.repository_uri and
  349. options.rss_path and
  350. options.rss_uri
  351. require "rss/1.0"
  352. require "rss/dublincore"
  353. require "rss/content"
  354. require "rss/maker"
  355. include RSS::Utils
  356. output_rss(options.name,
  357. options.rss_path,
  358. options.rss_uri,
  359. options.repository_uri,
  360. info)
  361. end
  362. end
  363.  
  364. begin
  365. main
  366. rescue Exception
  367. _, _, to, *rest = ARGV
  368. to = [to]
  369. from = ENV["USER"]
  370. begin
  371. options = parse(rest)
  372. to = options.error_to unless options.error_to.empty?
  373. from = options.from
  374. rescue Exception
  375. end
  376. sendmail(to, from, <<-MAIL)
  377. From: #{from}
  378. To: #{to.join(', ')}
  379. Subject: Error
  380.  
  381. #{$!.class}: #{$!.message}
  382. #{$@.join("\n")}
  383. MAIL
  384. end
Add Comment
Please, Sign In to add comment