Tracの複数プロジェクトの管理って皆さんどうしてます?

Trac導入前に、何年かSVNが運用されていました。そして、リポジトリはプロジェクトごとに分割していましたとさ。現在20ぐらいリポジトリがあったりしています。
サーバーが古くて、OSがMiracle3.0で、Tracとか入れられなくて悔しい思いを。
やっと新しいサーバーをGetしてリポジトリ移行して、それで後からTrac入れましたよ。やったー。やっぱりTracいいな。
けれどさ、なんかたくさんのTracプロジェクトがあって見通し悪いなぁ。
みんなはどうしているのだろう。ググる


・・・ん。
TraMとか、便利そうなものがあるみたいですけど、プロジェクトのURLを開いても真っ白。
あらら。

うーん。とりあえず、TracのTimeLineのRSSをまとめて、時系列で見れるようにしておけば、直近の動きは、まあまあ見えるのかなぁ。個別のRSSだと全体のタイムラインの把握は無理だぁ。あのライブラリの修正がおわったらpom.xml書き換えようとか、そういうの把握したいなぁ。
あと、Basic認証かけてるので、それをかいくぐってRSSをまとめたい。それに、他の開発者にも集約RSSを見えるようにしておけば、各自が複数のフィード登録しなくても済むしいいな。
じゃあ、イントラで使える、RSSあぐりげーたーでBasic認証できそうなやつないかな。あれれぇ、、、探せなかった。


というわけで、作ってみました。
TracのタイムラインのRSSを各プロジェクトから取ってきて、それらを時系列に並べて集約したRSSをつくるだけのシンプルなものです。CentOS5のサーバー用に作りましたので、Cronで定期実行されるようにして運用しています。

環境

# wget http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-2.noarch.rpm
# rpm -ivh epel-release-5-2.noarch.rpm 
# yum install rubygems
  • gem install mechanizeとかしておく。
    • Proxy設定が必要なら、環境変数http_proxyを設定。http://を省略したら駄目。
export http_proxy=http://proxy.example.com:8080

コード

#!/usr/bin/ruby
require 'rubygems'
require 'mechanize'
require 'logger'
require 'rss'

UID = 'hoge' #Basic認証のUserID
PASS = 'hoge' #Basic認証のPassword
URLBASE = 'http://server_ip/trac' #tracのプロジェクト一覧のURL
TRACHOME = '/var/www/html/trac' #tracのプロジェクトを保存した親ディレクトリ
TIMELINERSS = 'timeline?milestone=on&ticket=on&ticket_details=on&changeset=on&max=50&daysback=90&format=rss'
OUTDIR='/var/www/html/tracfeeds'
AGGREGATED = 'allfeeds.rdf'

dirs = `ls -1 #{TRACHOME}`
projects = dirs.split("\n")
feed_urls = {}

#WWW::Mechanize でBasic認証+各プロジェクトのRSSを取得
agent = WWW::Mechanize.new
projects.each { |proj|
        agent.auth(UID,PASS)
        agent.get("#{URLBASE}/#{proj}/login")
        feedurl = "#{URLBASE}/#{proj}/#{TIMELINERSS}"
        feed_urls.store(proj, feedurl)
        rss = agent.get(feedurl)
        rss.save_as("#{OUTDIR}/#{proj}_timeline.rdf")
}

#取得したRSSを読み込んで、Itemを抽出します。
allItems = []
Dir.foreach(OUTDIR) {|file|
        if ( file == "." || file == ".." || file == AGGREGATED)
                next
        end
        begin
                feed = RSS::Parser.parse(File.read("#{OUTDIR}/#{file}"),false)
                feed.items.each { |item|
                        item.title = "#{feed.image.title}: #{item.title}" #feed.image.titleはリポジトリ名がセットされるようなので、集約RSSのタイトルの先頭に表示するリポジトリ名として流用しています。
                        allItems.push(item)
                }
        rescue
        end
}

#抽出したItemから集約したRSSを作ります。
channel_desc_links = []
feed_urls.each { |proj,feed|
        channel_desc_links.push("<a href=\"#{feed}\">#{proj}</a>")
}
aggregated = RSS::Maker.make("2.0") do |maker|
        maker.channel.title = "trac all timeline feeds"
        maker.channel.link = "http://172.24.112.50/trac"
        maker.channel.description = "Tracのタイムラインを集約したRSSです\n" + projects.join("\n")
        maker.image.title = "dev 50 trac timelines"
        maker.image.url = "http://172.24.112.50/common/trac_banner.png"
        maker.items.do_sort = true #更新順にソートする
        allItems.each { |add|
                maker.items.new_item { |new|
                        new.link = add.link
                        new.title = add.title
                        new.date = add.date
                        new.description = add.description
                }
        }
end

open("#{OUTDIR}/#{AGGREGATED}",'w') { |f|
        f.puts aggregated.to_s
}

ちょっと運用してみたところ、まあまあ使えました。リポジトリをまたがって開発するなら便利かも。あと、上司に対してこんなに活動していますとアピールできたり。

根本的にリポジトリを整理できればいいのですけどね。