読者です 読者をやめる 読者になる 読者になる

gitで管理しているvimrcをrakeで一発デプロイできるようにした

前々からだらしなく管理されていたvimrcを始めとするconfigsですが、中途半端にsubversionにコミットしていたり、どこかの環境だけ新しかったりしたので、なんとかしたくてconfigsを整理していました。
この機にgitに乗り換えるのは先日書いた通りなのですが、gitに乗り換えただけでは中途半端な状態は解消されないので、どうしたら良いか考えました。

問題だった事

  • vimrcを書き換えで「ちょっと試してみよう」「一時的な設定」が多すぎた
  • commitがおざなりになっていた。
    • HOMEディレクトリのvimrcやpluginを編集してしまっていて、リポジトリとの整合性確認が面倒だった
  • なにがどの状態だったか記憶できていない。

デプロイ or シンボリックリンク

皆さんconfigsの管理ってどうしているのかな??と気になって

http://www.lingr.com/room/vim-users.jp

でセオリーがあるのか質問してみた所、意見が2つに別れました*1。というか派が存在。*2

ujihisaさん派 => シンボリックリンク
kanaさん派 => デプロイ

話を聞く前にはシンボリックリンクが楽そうなイメージだったけど、話を聞いてBuildデプロイで自動化というのもいいなと思って、ujihisaさんの囁きでrakeを使って自動化する事に決めました。

rake

makeのruby版みたいなもの。
Rakefileでbuildのルールとかタスクとか色々書ける。

rakeを叩いた事はありますが、書いた事はなかったので調べながらこつこつと。
依存関係とかルールとか書けてすごく便利な印象です。

これからも使っていきたいツールになりました。

必要なもの

  • Ruby
  • rake
    • rakeをインストールする為にRubygemsが必要

rakeはgemを使ってインストールします。

gem install rake

前提

できること

  • gitで管理しているconfigsをホームディレクトリにBuildデプロイする
  • デプロイに含めたくないファイルパターンを指定
  • リポジトリから削除されたファイルはHOMEからも削除される
    • configsと同階層のファイルとディレクトリは削除対象外。*3

タスク一覧

rakeコマンドを実行することでデプロイを実行することができます。

rake

デフォルトタスクはupdateタスクを実行します。

rake update

rakeを実行するカレントのconfigsワークディレクトリからBuildを行います。

rake rep_build

リポジトリから最新のソースコードを取得し、リポジトリの状態のファイルをBuildします。
configsリポジトリは編集中で、一時的にリポジトリの状態に戻したい時に使います。

rake rep_update

rep_buildとデプロイ条件は同じですが、こちらはcloneしたリポジトリをホームディレクトリのconfigs_workに残しておきます。
タスクを実行した日付のディレクトリが作成されます。

rake work_dir_remove

configs_workを削除します。
普段は単体であまり使うことがないサブタスクです。

rake package

違うホストにconfigsを移したい時に利用します。
ホームディレクトリのconfigs_work/pkg/にzipファイルが作成されます。
本来のpackageの使い方だとpackage→installコマンドでデプロイする形になると思うのですが、オレオレタスクになっています。。。

コード

とりあえず自分がしたい最低限のデプロイはできるようになりましたが、rakeっぽい書き方ができていなかったりとかコードの突っ込みどころも色々とあると思いますが、とりあえず公開します。

自分的にはこれでvim生活をLife Changingできました。
うひょー。

# Config Build Rakefile

require "find"
require 'rake/packagetask'

# 定数値
NOW = Time.now.strftime("%Y%m%d%H%M%S").to_s
HOME = File.expand_path("~")
EXCLUDE_FILES = Regexp.new("pkg\/|\.git\/|\.DS_Store|\.swp$|\.swo$|\.back$|Rakefile.rb$")
GIT_REPO = "git@guyon.unfuddle.com:guyon/configs.git"
unless HOME
    p "Not found home direcotry. Please set home envroiment varibale."
    exit
end

# タスクの関連付け
task :default    => ["update"]
task :rep_build  => ["rep_clone","update","work_dir_remove"]
task :rep_update => ["rep_clone","update"]

desc "カレントディレクトリのConfigsからHOMEディレクトリにBuildする"
task "update" do
    src_path = File.expand_path("./")
    dst_path = File.expand_path(HOME)

    # --配布
    file_copy src_path, dst_path

    # --削除
    # リポジトリ直下のディレクトリのみ削除対象とする
    Dir.foreach(src_path) {|file|
        if File.ftype(file) == "directory" && file.to_s !~ /^\.$|^\.\.$/
            remove_src_path = src_path + "/" + file.to_s
            remove_dst_path = dst_path + "/" + file.to_s
            file_remove remove_src_path, remove_dst_path
        end
    }
    p "Update Finish."
end

desc "最新バージョンのconfigsリポジトリを作成する"
task "rep_clone" do
    work_dir = HOME + "/configs_work/" + NOW
    mkdir_p work_dir
    cd work_dir
    sh "git clone #{GIT_REPO}"
    cd "./configs"
end

desc "workディレクトリを削除する"
task "work_dir_remove" do
    remove_dir = HOME + "/configs_work/"
    rm_rf remove_dir
end

Rake::PackageTask.new("configs",NOW) do |p|
    p.package_dir = HOME + "/configs_work/pkg/"
    p.need_zip = true
    # リポジトリ直下のディレクトリのみ削除対象とする
    Dir.foreach("./") {|file|
        next if file.to_s =~ EXCLUDE_FILES
        if File.ftype(file) == "directory" && file.to_s !~ /^\.$|^\.\.$/
            p.package_files.include(".*")
            p.package_files.include(file.to_s + "/**/*")
            p.package_files.exclude('**/*~')
            #TODO:パターンを定数値化する
            p.package_files.exclude("pkg/**",/^\.git$/,/\.git\//,".DS_Store",/\.swp$/,/\.swo$/,/\.back$/,"Rakefile.rb")
        end
    }
end

def file_copy(src_path,dst_path)
    Find.find(src_path){|src_file|
        next if src_file.to_s =~ EXCLUDE_FILES
        if File.file?(src_file)
            match = Regexp.new("^" + src_path)
            dst_file = dst_path + src_file.sub(match,"")
            if !File.exist?(dst_file)
                dir = File.dirname(dst_file)
                if !File.exist?(dir)
                    FileUtils.mkdir_p(dir)
                end
                FileUtils.cp(src_file,dst_file,:preserve=>true)
                puts "copy new " + src_file + " => " + dst_file
            elsif FileUtils.cmp(src_file,dst_file)==false
                FileUtils.cp(src_file,dst_file,:preserve=>true)
                puts "copy rewrite " + src_file + " => " + dst_file
            end
        end
    }
end

def file_remove(src_path,dst_path)
    return unless File.exist?(dst_path)
    Find.find(dst_path){|dst_file|
        match = Regexp.new("^" + dst_path)
        src_file = src_path + dst_file.sub(match,"")
        if !File.exist?(src_file)
            FileUtils.rm_rf(dst_file)
            puts "remove " + dst_file
        end
    }
end

10月26日追記

  • buildの記述をデプロイに修正。buildはしてないよねと・・・
  • rep_cloneは使わない方向に修正予定。
    • 無駄にgitリポジトリをコピーをしてしまっているのと、オフラインで問題が発生する。

*1:と言っても話は3人でした

*2:方法より人が中心w

*3:削除することはほぼないので、必要なときは手動で