SVN/GITリビジョン区間の更新ファイルをパッチ化する
はじめに
SVNもしくはGITにおけるリビジョン区間*1の更新ファイルをパッチ化*2する作業が生じたためシェル化メモ。
やりかた(SVNの場合)
# リビジョン区間(r6000-r6100)における更新ファイルについて、 # ディレクトリ構造を維持したままtar.gzに固める svn diff -r6000:6100 --summarize \ | awk '{print $2}' \ | (mkdir patch_dir; \ xargs -I{} cp --parents {} patch_dir; \ tar czvf patch.tar.gz patch_dir; \ rm -Rf patch_dir)
補足すると,以下の様な流れ
- 1行目で、リビジョン区間(r6000からr6100)の更新ファイル一覧を取得
- 2行目で、更新ファイル名のみを抽出
- 3行目で、作業パッチディレクトリを作成
- 4行目で、更新ファイルそれぞれについて、ディレクトリ構造を維持たままパッチディレクトリにコピー
- 5行目で、パッチディレクトリをtar.gzに固める
- 6行目で、作業パッチディレクトリを削除
やりかた(Gitの場合)
# gitでリビジョン区間(リビジョンハッシュA-リビジョンハッシュB)における更新ファイルについて、 # ディレクトリ構造を維持したままtar.gzに固める git diff --name-only リビジョンハッシュA..リビジョンハッシュB \ | (mkdir patch_dir; \ xargs -I{} cp --parents {} patch_dir; \ tar czvf patch.tar.gz patch_dir; \ rm -Rf patch_dir)
補足:SVNでリビジョン区間の更新ファイル一覧を最終更新リビジョンとともに取得
# リビジョン区間(r6000-r6100)の更新ファイル一覧を最終更新リビジョンとともに取得 svn diff -r6000:6100 --summarize \ | awk '{print $2}' \ | xargs -I{} bash -c "( svn ls -v -R {} | awk '{printf \$1}'; echo ' '{} )"
技術的負債をいかに減らすか
はじめに
技術的負債が大きくなってしまったコード(=保守しにくい品質が低いコード)をいかに改善するか?という話です.コードの保守性を定量評価したいとき、どういう指標を用いるのがいいんだろう??
— Kensho Fujisaki (@ni66ling) 2015年4月19日
コミット時にその指標が悪化してたらリモートリポジトリへのコミットを禁止にしたり、コミッター別にその指標に対する影響度をグラフ化して、技術的負債を改善したい
対処策として,リファクタリングやテスト充実化(★)すればいいじゃないか?という意見が出てくると思いますが,
これを漠然と実行に移すのは,意外と簡単なことではないのではないかなと思っています.
実行に移しにくい理由として,大きく以下を挙げます.
[理由1] ★は短期的に見ると製品価値を生む行為ではないため,関係者(経営者)に対して説得力しにくい
[理由2] 実施後のモチベーションが続きにくい
ここでは,世間的にこの問題に対してどのように対処しているのかを簡単にまとめ,
その上で,どのように対処すべきか考えをまとめたいと思います.
事例
mixi http://alpha.mixi.co.jp/entry/2012/11552/alpha.mixi.co.jp
①ソースコード静的解析による技術的負債の見える化(定量化)
②①の指標改善度により開発者を評価ドワンゴ doda.jp
①技術的負債を短期間返済するための部署立ち上げ
②GitHub/IntelliJなどの開発ツール導入GMOペパボ http://engineer.typemag.jp/article/pepabo-devengineer.typemag.jp
①技術的負債返済のための部署を社長直轄チームとして立ち上げ
②CI導入
③技術的負債返済に関して開発者を評価
各事例における対処策の分類
1.~3.の対処策をまとめると,以下の5点に分類できると思います.
- 技術的負債返済のための部署立ち上げ
- ソースコード静的解析による技術的負債の見える化
- CIによる技術的負債の継続的改善
- 技術的負債返済に連動した開発者評価
- GitHub/IntelliJなどの開発ツール導入
どのように対処するか
はじめの「実行に移しにくい理由」に対して対処できるのではないかなと思います.
[理由1] → 2.による現状把握と,この指標によるデメリットの説明
[理由2] → 3.,4.によるモチベーション維持
対処策を具体化すると,次のようになるかなと思います.
- 2.について
Rubocop*1による各種指標*2を取得
取得した指標について,Rubocop規定値および世間的な標準値をもとに現状を評価し,本問題の重要度を明文化 - 3.について
2.の指標をJenkinsで管理.具体的には,RSpecの自動テストと同様に,コミット時にRubocopで各種指標の取得を実施 - 4.について
3.で取得した値について,各種指標の改善度/改悪度をチーム全体およびコミッター別に計上
技術的負債返済の進捗状況を透明化
以上を実際に試してみて,技術的負債の返済をトライしたいと思います.
*2:Cyclomatic complexity, Assignment branch condition size metric, Perceived complexityなど.http://www.rubydoc.info/gems/rubocop/0.27.0/RuboCop/Cop/Metrics
ローカルのシェルスクリプトをリモート実行する
はじめに
「ほぼ」同じ構成のサーバがN台あって,それぞれに対して同じスクリプトを実行したい。このときスクリプトはひとつに集約したい(=各サーバにスクリプト配置したくない)。こういうときって、どうするのが定番なんだろう・・・
— Kensho (@ni66ling) February 14, 2015
ローカルのスクリプトをリモートで実行したいけどどうやれば?という話.
どうやるのが定番なのか?は結局わかっていないが,一応できるにはできたのでメモ.
やりかた
sshコマンドにパイプでローカルのスクリプトを引き渡す.*1
$ cat [local_script.sh] | ssh [user]@[host] bash
補足: ローカルにおける変数をリモートスクリプト内で使うには
プロセス置換*2を用いる.
$ param="local" $ cat <(echo -e "param=\"${param}\"\n") [local_script.sh] | ssh [user]@[host] bash
ただし,懸念点として,リモート実行するスクリプト内容が分散し,見通しが悪くなってしまう問題がある.
もっといい方法をご存知の方がいらっしゃったらお教えいただけると嬉しいです.
補足: cronやjenkinsからの自動実行するには
パスワード認証を自動突破するためにexpectを用いる.*3
ソースコードで示すと以下のとおり.
#!/bin/sh # ローカルのスクリプト(リモート実行する) remote_command_file="./local_script.sh" # expectでSSH接続 ([hoge]は適宜読み替えて) expect -c " set timeout -1 log_file $log_file spawn bash -c \"cat $remote_command_file | ssh [user]@[host] bash\" expect \"password\" ; send \"[password]\n\" expect eof # 注:interactとしないこと exit # 注:interactとしないこと "
expectに関する注意として,interact
とはせず,expect eof; exit
とすること.
ターミナルから実行する場合はinteract
で良いが,cronやjenkinsから実行する場合はexpect eof; exit
とする必要がある.*4*5
*1:注意点として,パイプで渡されるsshでは,bashを引数にすること.bashを引数にしなくても実行はできるが,標準エラー「Pseudo-terminal will not be allocated because stdin is not a terminal.」が出力されてしまう.また,さらに補足すると,この標準エラーは多段接続時に「-t」パラメータを指定しなかった場合にも出力される.
*2:http://sechiro.hatenablog.com/entries/2013/08/15
*3:公開鍵認証かつパスワードなしの設定であれば,expectを使う必要はない.$ ssh -i [鍵path] [user]@[host] bash, もしくは, sshのconfigファイルを読ませるなど
*4:http://yu-write.blogspot.jp/2013/11/shell-crontabexpect.html
*5:ここでかなりハマった…
before_filterをすべてスキップする
はじめに
子クラスで親クラスのbefore_filterをすべて実行させたくない状況が生じたのでメモ。
やりかた
skip_before_filter(*_process_action_callbacks.select{|filter| filter.kind == :before}.map(&:filter))
まず、skip_before_filterは、フィルタのシンボルを渡すとスキップできる。*1
フィルタのシンボルは、_process_action_callbacksから抜き出す。*2 *3
これは配列で返ってくるので、*で配列の展開をしている。*4
おまけ
子クラスのbefore_filterを先に実行し、その後に親クラスのbefore_filterを実行するには?
before_filterは、引数の順序にしたがって実行される。
よって、子の後に親を実行するには、1)親クラスのbefore_filterをすべてスキップし、2)子クラスのbefore_filterを実行し、3)親クラスのbefore_filterを実行すれば良い。
具体的には以下のとおり。
super_before_filters = _process_action_callbacks.select{|filter| filter.kind == :before}.map(&:filter) skip_before_filter *super_before_filters before_filter :child_before_filter, *super_before_filters
同じ考え方で、子クラスのbefore_filterを親クラスのbefore_filterの合間に実行したり、親クラスのbefore_filterを部分的に実行したりできる。