2012年4月8日日曜日

[python][php] tagファイルを読んで、依存関係のあるファイルを抽出するbzrプラグイン。

前から作ろうと思ってけど、いまいちのらなくて作ってなったやつ。

背景


今の開発環境では、プラットフォームが3つあり、
それぞれに本番環境、stg環境がある。
bazzarのレポジトリとはプラットフォームで共通のものを使っている。
stg環境では、レポジトリに含まれるプログラムが動作しており、
本番環境では、stg環境の中から展開されたものだけが動作している。
※ 展開は、ファイル・リビジョン番号を指定して、stg環境から本番環境にプログラムを移すことをさす。

そのため、本番環境のプログラムはプラットフォームごとに異なるが、
stg環境のプログラムはプラットフォームで共有となっており、
実質、3つの本番環境と1つのstg環境で運用していることになる。

ある機能について「プラットフォームAでは今日出すが、プラットフォームBでは来月出す」ってときは、
Aプラでは本番環境に展開されているが、
Bプラでは本番環境にはプログラムが展開されていないという状態になる。。
そして、Bプラでその機能を公開するときは、stgのプログラムを本番環境に展開する。

このとき、展開しておかなきゃいけなかったプログラムを展開し忘れる、展開もれが発生することがある。
もれるともちろん、参照する先のファイル、クラス、ファンクションがないので、
大量のエラーログがはかれることになる。
とくに、依存関係のあるプログラムで起こりやすい。
「え、これも展開しなきゃいけないの!?」って声はちょくちょく聞く。

「完全にレポジトリ分ける」か、
「Aプラが出すときにプログラムは全プラ一緒に出して、機能は公開しない」ってこと
ができればいいんだけど、どちらも現状難しそう。
直近対応として、「あるプログラムに対して依存関係のあるプログラムをリストにする」bzr pluginを書いた。
これを使って、依存関係のある全ファイルの、本番との差分を確認すれば展開漏れはすくなるなる気がする。


流れ

① あるリビジョン番号で追加・修正されたファイルを抽出する。
② それらのファイルから、参照しているクラスを抽出する。
③ クラスファイルのパスをtagファイルから特定する。
④ 本番との差分を確認するコマンドを表示する。


ソース


ソースは以下。
gist: 2335403

これを、~/.bazzar/plugin/rel.pyとして保存すれば、使えるようになる。


以下、中身を備忘録として残しておくか。

① リビジョン番号からファイルを抽出
bzr deployの流用。
[python] 修正内容をscpで転送するbzrコマンド。

② クラスの抽出。
クラスメソッドとインスタンスをnewしているとこから、
正規表現でクラス名を抽出した。

    def get_class(self, file_obj):
        func_list = []
        for line in file_obj:
            
            # get by ::
            func = self.re_search(line, r'([A-Z]\w+)::')
            if func != None and not(func in func_list):
                func_list.append(func)
            
            # get by new
            func = self.re_search(line, r'new ([A-Z]\w+)')
            if func != None and not(func in func_list):
                func_list.append(func)
            
        return func_list

ここでは、クラス名の先頭が大文字である事を前提としている。
「クラスメソッドの参照」と「メンバ定数の参照」をどうやって区別すればよいかわからなかったため。。
つまり、TEST::test1()と、$test::TEST_NAMEの違い。
たいてい、クラスは大文字から始まって、変数は小文字にするでしょ?っていう逃げ。
そのうち何とかしよう。


② tagファイルの読み込み
全部読むとかなり長くなってしまいそうだったので、
上から順番に読んでいって、あったらそこで読むのやめている。

また、どうやらでふぉのctagsで作られるタグファイルは、
[A-Za-z]見たいな並び方で作っているようなので、
対象になるクラス名の先頭と1文字、タグファイル読んだクラスの中で一番新しいクラス先頭1文字を比較し、
タグのほうが進んでいたら、それ以降に対象になるクラス名は出てこないはずだから、
そこで読むのをやめている。

つまり、zzとかで始まる奴が遅くなる。
これもあって、クラス名の先頭は大文字で始まるやつのみに絞るってことをしてしまった。

    def get_ctags_file_path(self, func_name):
        # chk target file
        for row in self.CTREADER:
            
            # read one
            tmp = []
            if row[0] in self.CTDICT.keys():
                tmp = self.CTDICT[row[0]]
            tmp.append(row[1])
            self.CTDICT[row[0]] = tmp
            self.LATEST_ASCII_HEAD = int(hex(ord(row[0][0])),16)
            
            # search
            if func_name in self.CTDICT.keys():
                return self.CTDICT[func_name]
            
            # check ascii head
            func_name_ascii_head = int(hex(ord(func_name[0])),16)
            if self.LATEST_ASCII_HEAD > func_name_ascii_head:
                return
        return
self.CTREADERは、タグファイルをcsv.readerで読んだrederオブジェクト。
self.CTDICTは、今までよんだ内容を保存しとくやつ。


③ 本番との差分を確認
社内ではmakuoっていうデプロイコマンド?を使っている。
このコマンドをdryrunすると、本番のファイルと差分を確認してくれるので(タイムスタンプのみだっけ?)、
dryrunするためのコマンドを表示する。


使い方

① tagファイルの作成
    ctags "-f" "./tags" "--langmap=PHP:.php.inc" "--php-types=c+f+d+v+i" "-R" "./lib/"
    ctags "-a" "-f" "./tags" "--langmap=PHP:.php.inc" "--php-types=c+f+d+v+i" "-R" "./plugins/"
    ctags "-a" "-f" "./tags" "--langmap=PHP:.php.inc" "--php-types=c+f+d+v+i" "-R" "./apps/"

② コマンド実行
[karino@115x125x111x181 trunk]$ bzr rel 5

dependence_file

apps/frontend/modules/job/actions/actions.class.php
|
+-- Doctrine_Core
|   ['./lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Core.php']
|
+-- Request
|   None


config/ProjectConfiguration.class.php
|
+-- CoreAutoload
|   None

-------------------------------

makuo -n \
game12/./lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Core.php \
game12/apps/frontend/modules/job/actions/actions.class.php \
game12/config/ProjectConfiguration.class.php

-------------------------------


こんな感じ。


ToDo

① クラス抽出をもうチョット何とかする。
・一行に複数の候補があったとき、最初のクラスしか抽出できない。
・先頭が大文字のクラスしか対象にしていない。

② tagの読み方
もっと速くなる読み方検討。

③ 再帰的に調べる
そこまでやる必要ないかな。。

0 件のコメント:

コメントを投稿