手元のgitリポジトリ一覧をgithubのwatched reposとsyncする
先日書いた、複数のgitのレポジトリを巡回するスクリプトは結構便利なのだけど、「その日にwatchしたプロジェクトとかはcloneしてこないとupdateをチェックできない」という欠点があって、IRCとかで話題になって興味を持って、家に帰ったらチェックしようと思ってwatchにしておいたプロジェクトを、手でcloneしてくるとかいうふうにしていたのですが、watchにしておいてcloneし忘れることもままあります。
ということでAPI使ってwatchの情報をみて実行したディレクトリ以下に「プロジェクト名/.git」というディレクトリがなければ、cloneしてくるスクリプトを書いた。
環境によっては、jsonだけgemとかで入れる必要があるかも。
require 'uri' require 'pathname' require 'logger' require 'net/https' class API API_BASE = URI("http://github.com/api/v2") def initialize end def get_type type, path uri = get_type_uri type, path http_client = Net::HTTP.new(uri.host, uri.port) res = nil http_client.start do res = http_client.get(uri.path) end return res.body unless res.nil? end def get_type_uri type, path URI("#{API_BASE.to_s}/#{type}/#{path}") end def json path res = get_type 'json', path require 'json' JSON.parse(res) end end Repository = Struct.new("Repository", :name, :owner, :url) class Repository def self.from_hash hash args = self.members.map {|s| hash[s.to_s] } self.new(*args) end def clone_url "git://github.com/#{self.owner}/#{self.name}.git" end def push_url "git@github.com:#{self.owner}/#{self.name}.git" end # FIXME: how to get collaborator infomation? def can_push? owner return true if self.owner == owner end end class App def initialize args @user = args[:user] @logger = args[:logger] @client = API.new @basedir = Pathname('.').realpath end def run gitrepos = Pathname.glob("#{@basedir}/**/.git") json = @client.json("repos/watched/#@user") threads = [] json['repositories'].each do |repo| modified_fg = false repos = Repository.from_hash(repo) target_dir = gitrepos.find {|i| i.to_s =~ %r|/#{repos.name}/\.git$| } unless target_dir target_dir = repos.name cmd = "git init #{repos.name}" system_with_log(cmd) modified_fg = true end modified_fg = git_config_set_safe(target_dir, "remote.#{repos.owner}.url", repos.clone_url) if repos.can_push?(@user) modified_fg = git_config_set_safe(target_dir, "remote.#{repos.owner}.pushurl", repos.push_url) end if modified_fg system_with_log("git --git-dir=#{target_dir} remote update") end end end def git_config target_dir, key, val=nil config_cmd = "git --git-dir=#{target_dir} config #{key}" if val return system_with_log("#{config_cmd} #{val}") else return system("#{config_cmd} > /dev/null") # no logged for checking. end end def git_config_set_safe target_dir, key, val unless git_config(target_dir, key) return git_config(target_dir, key, val) end return false end def system_with_log cmd @logger.debug("execute: #{cmd}") system(cmd) end end def main app = App.new :user => 'walf443', :logger => Logger.new($stderr) app.run end main()
まだいくつか不満はあって、
- 自分はcodereposっぽく、~/project/#{lang}/#{project}みたいな構成にしているので、言語を推定してそのディレクトリ以下にcloneしてほしい
- プロジェクト名がp5-xxxxxだったら、p5をとりのぞいて、xxxxxという名前でcloneしてほしいとか、プロジェクト名は、CamelCaseがのぞましいとか。
- collaboratorに入れられているプロジェクトだと、pushurlを設定してほしいけど、repos/watchedのAPIからはそれっぽい情報がとれない
複数のforkされたプロジェクトをwatchしている場合は1つのリポジトリに複数のremoteを登録してほしい対応しました- unwatchした場合はどうしよう。。。
とかいった感じですが、ちょっと泥くさくなりそうではあるので、やや手抜きなところもありますが、このあたりが落しどころな気もする。