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

ハトネコエ Web がくしゅうちょう

プログラミングとかAndroid

git利用者に贈る、Subversionがダメな理由

このブログを始めてからだいたい2年が経ちました。
2年前はどんな記事を書いてみたのかと見てみると……

これですね。
このときはプログラミング始めたてで、まずは git の使い方を覚えようとしていたようです。

このように、ここ数年でプログラミングを始めるようになった方は git からスタートしていて、
逆に Subversion ( SVN )を触ったことがない方も多いのではないかと思います。

では、なぜ現在は Subversion でなく git が使われるようになったのでしょう?
数年前、あるプロジェクトに参加したとき Subversion を初めて使ったところ、
「これはたしかに使いづらい……」とわかり、git が広まった理由を感じられましたので、
git に慣れた者にとって Subversion がつらかったところについてまとめたいと思います。

1. コマンドが長い!

1-1. init

ます、バージョン管理のためのディレクトリを作成する init コマンドを比較してみましょう。
すでにプロジェクトフォルダーへ移動しているものとします。
また、今回は Windows PowerShell 上での実行を想定しています。

gitの場合:

git init

svnの場合:

# まずはローカルもしくはサーバー上に、バージョン管理に使うディレクトリを用意します
# git init との比較ですから、今回はローカル環境に管理ディレクトリを作成することにします
mkdir /svn_repos/project_name
svnadmin create /svn_repos/project_name
svn mkdir file:///svn_repos/project_name/trunk file:///svn_repos/project_name/branches  file:///svn_repos/project_name/tags -m "created default directories: trunk, branches, tags"

# バージョン管理に使うディレクトリを作りましたので、それを pull します
# svn では、git pull は svn checkout です
# "まだプロジェクトフォルダー内に何もデータがない場合"は、以下の1行で済みます
svn co file:///svn_repos/project_name/trunk

なお、Windows10 の PowerShell でおこなっていますので、
もしかすると以前のOSですと /svn_repos や file:///svn_repos といった省略型が認識できないかもしれません。
その場合は、 c:/svn_repos や file://localhost/c:/svn_repos に、それぞれ置き換えてください。

また、プロジェクトフォルダー内にデータがある場合は、
チェックアウトする前にプロジェクトフォルダーを一度コピーして隔離しておく必要があります。
こちらをご覧ください : svnコマンドでリポジトリ作成する手順 | rui live note

1-2. commit

コミットするときのコマンドを比較してみましょう。
ここはほとんど同じですが、基本の流れですので書いておきます。

gitの場合:

ni test.txt # ni : New-Item を意味する、PowerShell 独自のコマンド。bash での touch コマンドに相当
git add .
git commit -m "created a test file"

svnの場合:

# 先ほどチェックアウトしたことでプロジェクトフォルダー内に
# trunk フォルダーができました。データはそのフォルダー内に置いていきます
cd trunk
ni test.txt
svn add *
svn commit -m "created a test file"

1-3. branch

ブランチを作るときのコマンドを比較してみましょう。

なお、Subversionにおいてブランチを作るというのは、
trunk フォルダーのコピーを指定ディレクトリに作る作業です。
したがって、ブランチを作れば作るほど、どんどんバージョン管理ディレクトリ以下の容量は増えていきます。

また、タグもブランチと同様です。
言ってしまえば Subversion にタグやブランチの機能なんてものは無く、
ユーザーが『慣例的な使い方をする』ことで成り立っています。

詳しくはこちら : 今更聞けないSubversionの使い方 - Qiita

Subversion から git へ移行したくてもできない人が語る、
SVNリポジトリの容量がやたらでかくて……」というお話は、こういうところに由来します。
(その他に以下で述べる、ignore がしづらいため無駄なファイルが……、などの理由も加わるでしょう)

gitの場合:

# 方法1 : ブランチを作りつつそのブランチへ移動
git checkout -b new_branch_name

# 方法2 : 上記コマンドが一般的かと思いますが、いちおう素直にやるのなら、
# 以下のように、ブランチを作ってから移動
git branch new_branch_name
git checkout new_branch_name

svnの場合:

svn copy file:///svn_repos/project_name/trunk file:///svn_repos/project_name/branches/new_branch_name -m "create new branch"

このように、ブランチやタグを作る際にも、管理フォルダーへのURLが必要になります。
今回の例のようにローカル環境に管理フォルダーがある場合はまだいいのですが、
これが社内サーバーだったりすると、いちいちURLを探してコピペしてくるのが面倒だったりします。

git remote set-url コマンドのありがたさを理解しました。

2. ローカルコミットができない

先ほど書いたコミット、ほとんど同じと書きましたが、中身は異なります。
git は commit してリモートリポジトリに push するという流れが一般的です。
一方 Subversion では、commit コマンドの段階で、checkout 時に用いたURLへの送信がおこなわれます。

つまり、Subversion の commit は、git の commit と push が一度におこなわれることを指します。
svn push というコマンドは存在しません。

一見、問題ないかに思えますが、チームでおこなうプロジェクトではなかなか困ったことになります。
書いているコマンドが常に完璧なわけではないですが、ロールバックを考えると、
なるべく頻繁にコミットすべきと git 利用者は考えます。
しかし Subversion では、コミットした途端にリモートリポジトリへの同期がおこなわれてしまいます。
当然、これではコンフリクトの機会も多くなり、コミットできない……コンフリクト解消しなきゃ……の作業を繰り返すことになります。

それから、ブランチを作る際も、git と違いローカルにバンバン作れません。
(仕組みを考えると、個人がローカルにブランチを作ることが可能なのですが、面倒なのでたいていリモートリポジトリにそのまま作っているかと思います)
git co -b を打つことがたびたびある git 利用者にとっては、なかなかつらさがあります。

以前Twitter『ブランチを作るのに申請書が必要』ブラック会社談義を見てどういうことかわからないでいましたが、
Subversion でならたしかに各人がバンバン作ると混沌化しますので、その規則が生まれるのもわからなくもないです。

3. ignore しづらい

.gitignore ファイルを作るのとはわけが違います。
これは説明が面倒ですので、Subversion での ignore の設定法は以下の2つの記事をお読みください。

Subversion/svn propset/ignoreの除外リストをファイルで管理 - yanor.net/wiki
SVNで特定のディレクトリ以下の更新を無視する - ふなWiki

設定が複雑なので、ignore の設定が正確におこなわれずに不要なファイルがコミットされてしまうことはよくあります。

4. コードレビューしづらい

GitHub もねぇ、BitBucket もねぇ、GitLab だってあるわけねぇ! の世界です。
調べたら、 CodeBarg というものがあるようですので、
もし会社が Subversion 環境だけどGitHub的コードレビューの慣習を入れたい、という場合は、
これの提案をおこなってみるといいかもしれません。
Codebrag·Subversion/Git対応のコードレビューシステム MOONGIFT

私が参加していたそのプロジェクトではコードレビューは存在しませんでした。
イサギイイネ・・・

5. コマンドだと大変なので結局GUIツールを使わざるを得ない

最初に書きました 1. の理由もあってか、たいていの人は TurtoiseSVN に落ち着きます。
TurtoiseSVN なら、ブランチ・タグの作成やコンフリクトの解消などが、わりとカンタンにおこなえるためです。

バリバリとコマンド入力をおこなうプログラマーの方にとっては、
GUIツールでバージョン管理をしていることで、体が腐っていく感覚が味わえるかもしれません……。

最後に

というわけで、git利用者が Subversion を使ってみたときのつらみについて書いてみました。
Subversion が必ずしも使いにくいかというと、Visual Studio を使っている人にとっては、TurtoiseSVN が統合されているために Subversion の方が手軽だったりする場合もあるようです。

しかし最近の Visual Studio、特に Visual Studio 2015 以降では git もそこそこ手軽に扱えるようになったようですね。
(参考 : Visual Studio ローカルでバージョン管理(git) - Qiita)

Subversion を使っているところには早く git に移行してほしいなと思うしだいです……。

DataDog に Ansible を連携させる ansible-datadog-callback の使い方

DataDogAnsible Integration が上手く動作しなくて頭を抱えました。

f:id:nekonenene:20170503232431p:plain

Configuration をよく読むと、 ansible-datadog-callback を入れなさい、と書いてあります。

なるほど、と設定しようとするもどこに何を置けばいいのかわからず困りました。
ローカル側に置くのかサーバー側に置くのかすらわからず迷いました。(答えはローカル側、つまりAnsibleを実行する側です)
以下、やり方です。

  1. Ansible の playbook があるのと同じフォルダに callback_plugins というフォルダを作ります。
  2. その callback_plugins フォルダの中に、
    ansible-datadog-callbackdatadog_callback.py を置きます。
  3. callback_plugins フォルダの中に datadog_callback.yml というファイルを新規作成し、
    以下のようにあなたの DataDog API キーを書きます。
    api_key: <your-api-key>
  4. datadog_callback.py の依存パッケージをインストールするため、以下のコマンドを実行してください。
    pip install datadog pyyaml
    datadogpyPyYAML がインストールされます )

あとは普段通りに Ansible を実行するだけです。

……で、終えられたら運がいいです。


私含めたいていの人は

ERROR! Unexpected Exception: No module named datadog

とエラーが出てくることに悩まされるでしょう。

「datadog インストールしたじゃん?!」と悩みます。

このエラーがなぜ発生するかというと brew install ansible で Ansible をインストールしている場合、
コールバックプラグインの実行に Ansible は自分自身が管理する Python を使い、ライブラリも Ansible 自身が管理するものを参照します。

/usr/local/Cellar/ansible/<ansible_version>/libexec/bin/python2.7

を使うし、

/usr/local/Cellar/ansible/<ansible_version>/libexec/lib/python2.7/site-packages

ディレクトリのライブラリのみ参照します。
このディレクトリには datadog ライブラリはありませんから、

ERROR! Unexpected Exception: No module named datadog

というわけです。

解決方法はいくらか考えられますが、ここでは2つ紹介します。

解決法1 : pip で Ansible をインストール

Homebrew で入れていることが問題と考える場合でのアプローチです。

以下のように、Homebrew で入れている Ansible をアンインストールし、 pip でインストールします。
この方法でも解決しますが、pip よりは Homebrew で Ansible のバージョン管理したいよなーと私は感じます。

brew uninstall ansible
pip install ansible

解決法2 : datadog ライブラリへのシンボリックリンクを置く

datadog ライブラリが Ansible 管理の Python ライブラリにないことが問題と考える場合でのアプローチです。

先ほど pip でインストールした datadog ライブラリへのシンボリックリンクを、
Ansible 管理の Python ライブラリフォルダに置きます。

ln -nfs /usr/local/lib/python2.7/site-packages/datadog /usr/local/Cellar/ansible/<ansible_version>/libexec/lib/python2.7/site-packages

(さっきから <ansible_version> と書いていますが、ここはお使いの Ansible バージョンに合わせて書き換えてくださいね)

これでも動くようになります。
問題点は、Ansible のバージョンを上げたときにまた同様にシンボリックリンクを貼る必要があることです。
その観点では解決法1のほうがいいですね。

ちなみに yaml ライブラリは Ansible 管理の Python ライブラリに含まれていますのでシンボリックリンクを貼る必要はありません。
ansible-datadog-callback のREADMEに書いてあったから pip install pyyaml としていますが、pyyaml はインストールしなくても良さげ。
pip install ansible の場合でも依存パッケージとして pyyaml は勝手にインストールされるので)

さて、これで実行できるようになっているはずです! おめでとうございます!

f:id:nekonenene:20170504003835p:plain

でもこの連携、使い所がいまいち多くないですね。

Ansible の実行結果はローカルで表示されるわけですから、実行結果を DataDog で見られるようにするメリットはそう感じられません。
となると、これが便利なのはサーバー側で別サーバーの Ansible Role を定期的に実行する場合などでしょうか?

例えば GitHub の master ブランチに変更があると CircleCI が走り Ansible Role を実行、
などとなっている場合には便利かもしれません。
成功・失敗程度は CircleCI からでも通知できますが、 Changed が指定した値より小さいときに通知なんてのは、
DataDog から通知設定するほうが楽でしょう。

使い方がわかり取れるデータがわかったところで、この連携は私の環境では不要なのでやめましたが、
Ansible Callback Plugins の導入方法としてはとてもいい勉強になりました。

Ansible で、コマンドが存在するかどうかで処理を変える方法

冪等性(べきとうせい)を考えて、
パッケージ(コマンド)を apt で初めてインストールするときだけ処理をしたい。
という要望が自分の中で湧いてきて、さてどうしようと調べるも、なかなか出てこない。

「already installed ansible」とGoogle検索かけたら以下のサイトがヒットして、とても役立ちました。

Ansibleで冪等性を保つためにはfailed_whenとかstatを使うと便利 - 銀色うつ時間

いろいろ試してみたら、もうちょっと直感的な見た目に書けましたので、
それをご紹介。

それにあたりこちらのサイトも役立ちました。

ansibleでの実行結果のsuccess/failedとignore_errors/failed_whenがよくわからなかったので調べてみた - Qiita

さて、どうなったのか、以下に書きます。

- name: check exist mysql command
  shell: type mysql
  register: exist_mysql
  failed_when: false

- set_fact:
    is_exist_mysql: "{{ exist_mysql | success }}"

- debug: msg="{{ is_exist_mysql }}"
  when: is_exist_mysql == true

これを実行すると、 mysql コマンドがすでに実行できるときはデバッグメッセージとして

ok: [123.456.78.90] => {
    "changed": false,
    "msg": true
}

と出ますし、逆に存在しなければ

skipping: [123.456.78.90]

と出力されます。

failed_when: false でなく ignore_errors: true と書いてもいいのですが、
ignore_errors は処理は続行されるのですが、エラーメッセージが出力されてしまって、
ちょっと心臓に悪いので、エラー扱いにならない failed_when: false を使用しています。

なお、 shell: type mysql としていますが、当然 shell: which mysql でも構いません。好みです。
(参考 : Bashでコマンドの存在チェックはwhichよりhashの方が良いかも→いやtypeが最強 - Qiita


ではでは、作業中なので要点だけで失礼…。

ansible で冪等性を保証するの、難しすぎて苦戦中です……。

続・SMF ( Standard MIDI File ) Format1 のバイナリを読んでみた

前回の記事の続きです。↓前回の記事

4D 54 68 64 00 00 00 06 00 01 00 02 03 C0 4D 54 
72 6B 00 00 00 17 00 FF 03 00 00 FF 51 03 06 8A 
1B 00 FF 58 04 04 02 18 08 00 FF 2F 00 4D 54 72 
6B 00 00 00 2B 00 FF 03 00 00 FF 21 01 00 00 B0 
79 00 00 B0 00 00 00 B0 20 00 00 C0 28 00 B0 07 
64 9E 00 90 3C 64 9E 00 80 3C 00 9E 00 FF 2F 00

前回の記事では、このバイナリファイルのヘッダーとコンダクタートラックまでを読みました。
3行目の13列目、つまり以下のところまでです。

4D 54 68 64 00 00 00 06 00 01 00 02 03 C0 4D 54 
72 6B 00 00 00 17 00 FF 03 00 00 FF 51 03 06 8A 
1B 00 FF 58 04 04 02 18 08 00 FF 2F 00

今回はその続きを読んでいきましょう。
前回の記事が前提知識となりますので、未読の方は前回の記事を先にお読みください

トラック(演奏トラック)

4D 54 72 
6B 00 00 00 2B 00 FF 03 00 00 FF 21 01 00 00 B0 
79 00 00 B0 00 00 00 B0 20 00 00 C0 28 00 B0 07 
64 9E 00 90 3C 64 9E 00 80 3C 00 9E 00 FF 2F 00

これがひとつの演奏トラックです。
Domino(MIDI編集ソフト)で見ると以下のようになっている箇所です。

f:id:nekonenene:20170225194904p:plain

開始宣言

4D 54 72 6B
これを、ASCIIコード表に沿って直すと MTrk
「ここからトラックについての記述が始まるよー」というサインになります。

トラックの長さ

00 00 00 2B
10進数に直すと 43 です。
「ここから 43 Byte 続くよー」と言ってくれています。

トラックのタイトル

00 FF 03 00
トラック名の指定です。
前回のコンダクタートラック同様、何も名付けていません。

ポート番号の指定

00 FF 21 01 00
ここから前回にはなかった新しい要素、
まずは「ポート番号の指定」です。

「FF 21」で「ポート番号の指定を始めますよー」という合図、
次の「01」が「それに1Byte使うよ」という宣言で、

最後の「00」で「Port A」だと言ってくれています。
Port Bの場合はここが「01」になります。

リセットオールコントローラー (CC#121)

00 B0 79 00

最初の「00」は0Tick経過したところで、の意味。(前回記事参照)
そして次の「B0」、これは「チャンネル1にコントロールチェンジを送るよ」という合図です。

最初の「B」がコントロールチェンジを指すサイン
次の「0」がチャンネル1を表しています。

なので、チャンネル2にコントロールチェンジを送るなら「B1」、
チャンネル16にコントロールチェンジを送るなら「BF」というふうになります。

そして、その次の「79」がコントロールチェンジ番号を表していて、
「79」を10進数に直すと121(7 * 16 + 9 = 121)。
コントロールチェンジ番号121「リセットオールコントローラー」を指します。

この信号でピッチベンドや音色などは初期値にリセットされます。
「79」の次にある「00」はValueで、リセットオールコントローラーの場合は別にどんな値でもいいんですが、通常は「00」としておきます。

バンクセレクトMSB (CC#0)

00 B0 00 00
「チャンネル1にコントロールチェンジ0番を送るよ」という合図になります。

コントロールチェンジ番号0は「バンクセレクトMSB」です。

MIDIの各チャンネルの音色は、
プログラムチェンジ と MSB と LSB の組み合わせ
によって決まります。
それぞれ128種類あり、MIDI音源は最大で 128 * 128 * 128 = 2,097,152 音色持てる計算になります。

今回の場合は、
プログラムチェンジ : 41(バイオリンの音色)
MSB : 0
LSB : 0

で、実際、「B0 00」のあとの最後の値(Value)は「00」です。

バンクセレクトLSB (CC#32)

00 B0 20 00
「チャンネル1にコントロールチェンジ32番を送るよ」という合図になります。

コントロールチェンジ番号32は「バンクセレクトLSB」です。
最後の値は「00」なので、LSB = 0 ということになりますね。

プログラムチェンジ

00 C0 28
チャンネル1にプログラムチェンジを送っています。
コントロールチェンジは「B」でしたが、プログラムチェンジは「C」で始まります。

ですので、チャンネル1にプログラムチェンジを送る合図は「C0」です。
その次の「28」が値(Value)で、10進数に直すと40……なんですけど、
ここの値は「00」(つまり0)から始まる一方、プログラムチェンジは1~128で規定されていますので、ここは41とお考えください。

GM規格ではプログラムチェンジ1~128が規定されており、
1 は Acoustic Piano、41 は Violin の音色です。
このチャンネル1はヴァイオリンの音色で鳴らされます。

ボリューム (CC#7)

00 B0 07 64
チャンネル1のチャンネルボリュームを100に設定しています。

「チャンネル1にコントロールチェンジ7番を送るよ」という合図で、
コントロールチェンジ番号7は「チャンネルボリューム」です。
最後の値(Value)は「64」なので、10進数に直すと100、トラックの音量は100です。
(なお、最大値は127です)

「ド」を鳴らす

9E 00 90 3C 64
設定が続きましたが、ようやく演奏の部分です。

最初の「9E 00」は「3840Tick経過したところで」の意味になります。

詳しくは前回記事のTickの項を読んでもらうとして、
9E - 80 = 1E で、これを10進数に直すと30。
「9E」の次は「00」で、「80」より小さな値ですからTickの値の指定はここまでとわかります。

よってTickは
128 * 30 + 1 * 0 = 3840 Tick と計算できます。

そして続くのが「90 3C 64」の部分。

「90」は「チャンネル1のノートON」を示します。
コントロールチェンジは「B」、
プログラムチェンジは「C」でしたが、
ノートON(音符の開始)は「9」が目印と決まっています。

そして音の高さは「3C」、10進数に直すと60、これはC4。MIDI音源にもよりますが、ト音記号のドの音です。
ヘ音記号のドの音はそこから12小さい48、16進数で表すと「30」の音です。

そして最後の値(Value)の「64」、10進数で表すと100であるこれが、
音の強さ(Velocity)の値を示しています。

「ド」を止める

9E 00 80 3C 00
先ほど鳴らしましたドの音を3840Tick経過したところで停止します。

「9E 00」が「3840Tick経過したところで」の意味、
そして次の「80」は「チャンネル1のノートOFF」を示します。
ノートON(音符の開始)は「9」でしたが、ノートOFF(音符の停止)は「8」というわけです。

で、どこのノートを止めるかというと「3C」の高さの音、
先ほど鳴らしました「ド」の音ですね。

そして最後の値(Value)が「00」です。
ノートOFFの音の強さ(Velocity)、「ノートオフベロシティ」は、
ごくごく一部のMIDI音源がリアルな演奏のために対応しているのですが、
ごくごくまれすぎるため、たいていのMIDI編集ソフトはここを0として処理します。

私がはるか昔に使っていた「Music Studio Producer」なんかは、ノートオフベロシティも編集しやすいようになっていましたね…。

トラックの終わり(End of Track)

9E 00 FF 2F 00
前回のコンダクタートラック同様、トラックの終了宣言をおこないます。

「9E 00」が「3840Tick経過したところで」の意味で、
「FF 2F 00」が「トラック終了(End of Track)」の意味です。

このトラックは4小節目の0Tick目で終了で、解説もこれで終了です。

おわりに

というわけで、2回にわたって続きましたMIDIファイルバイナリの解説、いかがでしたでしょうか!

バイナリって「難しそう」「マニア的なもの」という印象でしたが、
こうして覗いてみると、理路整然としていて思いのほかわかりやすいものであると感じました。(調べるの大変でしたけど…)
この記事を読んでくださった方も同じように感じてくださったのではないでしょうか?

こうなってくると png ファイルとか wav ファイルも気になってきますよね?
wav は読むの大変そうですが、でも気になる……。


最後に、今回参考にしたサイトを以下にまとめておきます。

ここまで読んでくださった方々、どうもありがとうございました!


そして流れを無視して貼っておくアフィリエイトリンク……
(接続部がよくある miniB端子でなく microB端子だからか、この1年くらい使ってるけど抜けにくくてけっこういい感じ)