指定したディレクトリ配下のgitリポジトリを再帰的にpullする

久しぶりの更新になってしまった…

rbenvやpyenvなど、いわゆる**env系のツールを常用しているのですが、これらのツールツールそのものも、プラグインもgitでインストールを行います。
すると、以下のようなディレクトリ構成になります。

$HOME/.rbenv
├ .git/
├ rbenv本体
├ ...
└ plugins/
  ├ rbenv-rehash
  ├ ...
  └ ruby-build
    ├ .git/
    ├ ruby-build本体

例えば、新しいバージョンのRubyがリリースされたので、ruby-buildの更新を行いたい、ついでにrbenv配下のプラグイン群も更新したいと思うと、それぞれのディレクトリ(.gitディレクトリがあるところ)にcdで移動してgit pullを実行しないといけません。

そこで、指定したディレクトリ配下のgitリポジトリにcdしてgit pullするスクリプトを書いて、.zshrcに登録してみました。名前はgitpull-rにした。

# 指定したディレクトリ配下を再帰的にgit pullする
function gitpull-r(){
    if [ $# -ne 1 ]; then
        echo "gitpull-r <directory>"
        return
    fi
    
    cdir=`pwd`
    
    destdir=`ruby -e 'puts File.expand_path("#{ARGV[0]}")' $1`
    
    for gitdir in `find $destdir -name ".git" | sed -e s/\.git$//g`
    do
        echo "== git pull results [$gitdir] =="
        cd $gitdir
        git pull
    done
    
    cd $cdir
}

動くけど、指定したディレクトリを絶対パスに変換するところをRubyワンライナーにしているところが気に入らない…。realpathってコマンドもあるらしいけど、デフォルトでは入っていないことが多いようなので、自分の開発環境なら間違いなく入っているRubyワンライナーにしました。

実行スクリプトの絶対パスを得る方法ならあるけど、コマンドライン引数に渡したパスを絶対パスに変換する方法を思いつかなかったので、手っ取り早くRubyの力を借りました。何かいい方法があるのかな。

ちなみに、git submoduleってのもあるらしいけど、あくまでgitリポジトリ間の依存関係を定義するためのもので、複数のリポジトリをまとめて最新にしたいみたいな用途で使うものではなさそう。

Ohaiが割と使えるかも

最近、今話題のChefでいろいろ遊んでいます(Chefの説明はいろんなところにあふれかえっているので省略)。

Chefの構成要素の1つに、Attributeというものがあります。
Attributeとは、Chefのクライアントが実行されたときに収集されるサーバの情報(IPアドレス、OS、カーネルのバージョン等)やCookbook(の中のattributes等)で指定したパラメータの集合のことで、Attributeを利用することによってサーバの情報やあらかじめ指定しておいた条件に基づいて動的にサーバ構築を行うことができます。
例:OSに応じてインストールパッケージを変える、指定した条件に一致する場合にだけ特定の処理を実行するなど

Cookbook内で指定するパラメータ以外に、自動的に設定されるAttributeが知りたくて調べていたんですが、どうもOhaiというソフトウェアがそのあたりの情報を収集しているみたい。
ohaiはChefに組み込まれていて、Chefをインストールすると一緒にインストールされます。

どんな情報を集めているのか調べるために、ohaiを実行してみました。

# ohai
[2013-03-20T22:45:38+09:00] INFO: [inet6] no default interface, picking the first ipaddress
{
  "languages": {
    "ruby": {
      "platform": "x86_64-linux",
      "version": "1.9.3",
      "release_date": "2013-02-22",
      "target": "x86_64-redhat-linux-gnu",
      "target_cpu": "x86_64",
      "target_vendor": "redhat",
      "target_os": "linux",
      "host": "x86_64-redhat-linux-gnu",
      "host_cpu": "x86_64",
      "host_os": "linux-gnu",
      "host_vendor": "redhat",
      "bin_dir": "/usr/bin",
      "ruby_bin": "/usr/bin/ruby",
      "gems_dir": "/usr/local/share/gems",
      "gem_bin": "/usr/bin/gem"
    },
...
    "loop4": {
      "size": "0",
      "removable": "0"
    },
    "loop5": {
      "size": "0",
      "removable": "0"
    },
    "loop6": {
      "size": "0",
      "removable": "0"
    },
    "loop7": {
      "size": "0",
      "removable": "0"
    }
  }
}
(出力行でだいたい3500くらい)

多いwww
でも、どのOSで実行しても同じ情報(OSやアーキテクチャによっては空のものもあるだろうけど)、同じJSONフォーマットにしてくれるなら利用価値があるかも。

ohaiが収集する情報のカスタマイズやフィルタリングについては、Qiitaの「Chefの心臓、Ohaiのアトリビュートを他のプログラムからも拝借したい」が参考になると思います。

Chef面白いですね。もっと勉強したい。

EmacsでASCII以外の文字を含むキーバインドを定義する

ちょっとはまったのでメモ。
Anythingを起動するためのキーバインドを「Ctrl + ;」としたくてこんな風に設定ファイルに書きました。

; AnythingをCtrl+;で呼び出す
(global-set-key "\C-;" 'anything)
でも、これでEmacsを起動すると、以下のようなエラーになります。

error: Invalid modifier in string
で、調べてみたところ、ASCII以外の文字を含むキーバインドを定義するには、以下のようにしないとダメらしい。
これで設定してみたらエラーは出なくなりました。

; AnythingをCtrl+;で呼び出す
(define-key global-map [?\C-;] 'anything)
でも、Ctrl+;を押してもAnythingが起動しない…なんで???って思ってたらこういうことらしいです。

ターミナルでC-,やC-.に割り当てたい

自分はEmacsを-nwオプションをつけてターミナルの中で使っているんですが、ターミナル経由だとASCIIの範囲でしかEmacs側に通知されないってことかな。
試しにターミナルの中ではなく、アプリ版(?)のEmacsをダウンロードして使ってみたら、Ctrl+;でもAnythingが起動しました。

上のリンクに裏技が書いてあってどうにかできるらしいけど、結局素直にASCIIの範囲内でのキーバインドにしました。

DevLOVE 2012に参加してきました。

12月15日、16日で開催された、DevLOVE 2012に参加してきました。
今まで参加してきた(と言ってもまだ数えるほどだけど)勉強会の中で一番よかった。とても勇気づけられました。

特に目から鱗だったのが、TDDに関する諸橋さんと和田さんのセッション。
今までTDDと言うと、

• プログラムが変更に強くなる
• 品質が良くなる
• バグの早期発見
リファクタリングが容易になる

っていうプログラムそのものに対する効果しか認識をしていなかった。
それはそれでもちろん重要なことだけど、実は開発者本人のモチベーションにとても大きな効果があることを学んだ。

諸橋さんのセッションだと、

• プログラムが自分の思い通りに動くかどうか確認する手助けになる。
• 「プログラムにこう動いて欲しい」という振る舞いを書くことで、思考を整理できる。

ことで、プログラムが思い通りに動いて楽しい!!ってことを経験できる手助けになる。

和田さんのセッションだと、

• テストを書いてプログラムへ変更を加えることのハードルを下げ、「動いているものに触るな」というタブーを破る。
• タブーを破ることで、「きれいなコードを書く」という開発者にとって名誉ある、やる気が出る仕事に取り組むことができる。

ことで、やる気を取り戻し、プライドを持って仕事ができる。

テストを書くっていうのが、開発者の「楽しい」とか、「仕事に自信を持つ」っていう「気持ち」をプラスにできるっていうのがとても、とても印象に残った。

小さなことでもいいので、自分も何か始めよう。
そう決心できた勉強会でした。

スタッフの方、登壇者の方、おつかれさまでした!

GRUB2でデフォルト起動するカーネルバージョンを変更する

久しぶりのブログになってしまいました…。

先日、開発用マシン(Fedora17)にVMware Player 5をインストールしたのですが、Playerは起動するも、仮想マシンを起動しようとするとすぐに落ちる、という問題が起きました。
調べてみると、kernel 3.5系では、こういう問題もあるらしいので、カーネルのバージョンを古いものにしてみたらうまく行きました。

GRUB2の設定を変更して、再起動時に古いバージョンのカーネルで起動するようにしたのでそのときのメモ。
環境は以下です。

OS
Fedora17(x86_64)
GRUB2
Ver2.0 0.38.beta6.fc17

っていうか、GRUB2は設定の変更方法が直感的じゃないような…なんとかならんのかこれ…

GRUB2メニューの構造を調べる

まずは、以下のようなコマンドを実行して、起動時のGRUB2のメニュー構造を調べます。

# grep "menuentry" /boot/grub2/grub.cfg 
if [ x"${feature_menuentry_id}" = xy ]; then
  menuentry_id_option="--id"
  menuentry_id_option=""
export menuentry_id_option
menuentry 'Fedora' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-60960b2d-1b31-4c62-9ee5-518dbc83c470' {
submenu 'Advanced options for Fedora' $menuentry_id_option 'gnulinux-advanced-60960b2d-1b31-4c62-9ee5-518dbc83c470' {
	menuentry 'Fedora, with Linux 3.6.3-1.fc17.x86_64' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.6.3-1.fc17.x86_64-advanced-60960b2d-1b31-4c62-9ee5-518dbc83c470' {
	menuentry 'Fedora, with Linux 3.6.3-1.fc17.x86_64 (recovery mode)' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.6.3-1.fc17.x86_64-recovery-60960b2d-1b31-4c62-9ee5-518dbc83c470' {
	menuentry 'Fedora, with Linux 3.4.4-3.fc17.x86_64' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.4.4-3.fc17.x86_64-advanced-60960b2d-1b31-4c62-9ee5-518dbc83c470' {
	menuentry 'Fedora, with Linux 3.4.4-3.fc17.x86_64 (recovery mode)' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.4.4-3.fc17.x86_64-recovery-60960b2d-1b31-4c62-9ee5-518dbc83c470' {

この実行結果から、メニュー構造が以下のようになっていると推測できます(再起動してみて実際に確認してもよいと思います)。

Fedora
Advanced options for Fedora Linux
├ Fedora, with Linux 3.6.3-1.fc17.x86_64
├ Fedora, with Linux 3.6.3-1.fc17.x86_64 (recovery mode)
├ Fedora Linux, with Linux 3.4.4-3.fc17.x86_64
└ Fedora Linux, with Linux 3.4.4-3.fc17.x86_64 (recovery mode)

ここでは、kernel 3.4.4-3(Advanced optionsの3番目)をデフォルトにすることとします。

GRUB2の設定を変更

GRUB2の設定は以下の2種類のファイルで変更できます。

/etc/defaults/grub
/etc/grub2.d/ 配下のファイル群

/etc/grub2.d/ 配下のファイル群はシェルスクリプトになっていて、これらのファイルをいじると細かな設定ができるらしいです。
今回は、 /etc/defaults/grub の方に変更を加えます。
自分の環境では以下のようになっていました。

# cat /etc/default/grub 
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Fedora"
GRUB_DEFAULT=saved
GRUB_CMDLINE_LINUX="SYSFONT=False rd.lvm.lv=vg_jewelbox/lv_root rd.dm=0 rd.md=0 rd.luks=0 LANG=ja_JP.UTF-8  KEYTABLE=us-acentos rd.lvm.lv=vg_jewelbox/lv_swap rhgb quiet"
#GRUB_THEME="/boot/grub2/themes/system/theme.txt"

なんとなく分かると思いますが、「GRUB_DEFAULT」を変更して、デフォルトの環境を変更します。
まぁここまでは普通というか、イメージできるんだけど、ここからが謎…。

このGRUB_DEFAULTに指定する値ですが、最初に調べたメニュー構造を基にして指定します。
kernel 3.4.4-3はトップメニューの2番目、サブメニューの3番目にあるので、以下のように指定します。

GRUB_DEFAULT="1>3"

で、この後、grub.cfgを更新します。
grub.cfgは /etc/defaults/grub や、 /etc/grub.d 配下のシェルスクリプト群から生成しているようなので、これらのファイルを変更しただけでは設定は反映されません。

# grub2-mkconfig -o /boot/grub2/grub.cfg

これで起動すればOK。
分かりづらい。トップメニューの方は0から始まって、サブメニューの方は1から始まってるのも気に食わない。

またベータバージョンみたいだし、仕方ないんですかね。

virtualenvwrapperでプロンプトに環境変数名を表示させないようにする(正式版)

先日の暫定版の記事を書いていていろいろ調べているうちに、もっとスマートな(こちらの方が適切な)やり方を見つけました。

というか、virtualenvwrapperのマニュアルに書いてました(汗)
マニュアルをちゃんと読んでから使えって話ですね。

http://www.doughellmann.com/docs/virtualenvwrapper/ja/scripts.html#scripts

virtualenvwrapperが提供しているコマンド(シェル関数)の実行前後に読み込まれるスクリプトがあり、それに設定を書くことによって環境のカスタマイズができるようです。
今回は、仮想環境の有効化(activate)の際にプロンプトの設定を変えたいので、「postactivate」にこのような設定を行います。

PS1="[%n]#% "

自分はプロンプトはシンプルにしたいのでこうしています。
これでvirtualenvwrapperで設定される仮想環境名を上書きして自分好みのプロンプトにできます。

(python27-env)[lapislazuli]% workon python27-env                                               [~] 
[lapislazuli]%                                                                                 [~] 

virtualenvwrapperでプロンプトに仮想環境名を表示させないようにする(暫定版)

virtualenv + virtualenvwrapperをデフォルトで使っている場合、アクティブな仮想環境がプロンプトに表示されます。


仮想環境名が python27-env の場合
(python27-env)[lapislazuli]%

今使っている仮想環境名が分かってこれはこれで便利だけど、自分はプロンプトに余計なものが表示されるのが好きではないです。
なので、表示させないようにしました。

その方法を紹介。

環境変数で設定しているだろうと思ったので、envコマンドを実行してみたら、PS1に仮想環境名を含めた名前を設定しているようでした。


(python27-env)[lapislazuli]% env                                                  [~/.virtualenvs]
...
(中略)
...
VIRTUAL_ENV=/Users/lapislazuli/.virtualenvs/python27-env
PS1=(python27-env)[%n]%(!.#.%%) 
...

virtualenvwrapperが提供しているコマンドはスクリプトなので、$WORKON_HOME配下にそれっぽいスクリプトがあると思い、grepしてみたらやっぱりありました。


(python27-env)[lapislazuli]% find . -name "*" -exec grep -Hn "PS1" {} \;          [~/.virtualenvs] 
./python27-env/bin/activate:24:    if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
./python27-env/bin/activate:25:        PS1="$_OLD_VIRTUAL_PS1"
./python27-env/bin/activate:26:        export PS1
./python27-env/bin/activate:27:        unset _OLD_VIRTUAL_PS1
./python27-env/bin/activate:56:    _OLD_VIRTUAL_PS1="$PS1"
./python27-env/bin/activate:58:        PS1="$PS1"
./python27-env/bin/activate:63:        PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
./python27-env/bin/activate:65:        PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
./python27-env/bin/activate:68:    export PS1

どうやら、activateっていうスクリプトの中でPS1環境変数に仮想環境名入りの文字列を設定しているみたいです。

PS1に代入しているところをコメントアウトしたらプロンプトに仮想環境名が表示されなくなりました。


(python27-env)[lapislazuli]% workon python27-env                                               [~] 
[lapislazuli]%                                                                                 [~] 

追記

ブログ記事を書きながら少し調べていたら、activateスクリプトを修正するのではなく、$WORKON_HOME直下にあるpostactivateやpostmkvirtualenvあたりをいじるのがもっと適切な方法らしいことが分かったので、記事を書きました。