Mercurial用のinfo.pyを更新した

Mercurialには便利なモジュールが幾つか有るが、その中のお気に入りの一つは下記の情報表示用のものだ。これを使えば、複数のコマンド経由で取得して得られる情報を、一括して表示してくれるので有り難い。内部的には複数のコマンドを呼出しているだけのことだけど、こんな気の利いたツールは、かゆいところに手が届くので手放せない。きっと、作者の方も(ワタシと同じように)面倒臭いことが嫌いなのだろう。

リポジトリの情報を確認する
Subversionで提供されているsvn infoは便利です。 Gitではgit config --listで確認します。 そこでMercurialでもhg infoを実現するInfo Extensionが作られています。 これはデフォルトでバンドルされていないので、モジュール(info.py)をダウンロードして設定します。

[extensions]
info = /path/to/info.py
Git使いがMercurial使いに転職するとき設定しておくべきMercurial拡張 - TIM Labs

しかしながら、そんな便利なツールも、最近はエラーを吐き出すようになっていた。

$ hg info
** Unknown exception encountered with possibly-broken third-party extension info
** which supports versions unknown of Mercurial.
** Please disable info and try your action again.
** If that fixes the bug please report it to the extension author.

おそらくMercurialの新しいバージョンに対応できていないだろうと思いつつ、更新版を探したところ、現在の配布元である下記のサイトに情報が載っていた。

Martin H〓cker: Mercurial 3.8.1 now warns about the command not registered using the decorator, locally for me this patch works. Feel free to remove it from this page once it is applied. (See https://bitbucket.org/dwt/hg-info/ for someting easier to clone from)

InfoExtension - Mercurial

指示に従って、下記にアクセスすると、確かに更新版が配布されていた。

動作環境

導入方法

  • 既存のinfo.pyと置き換えるだけ。(新規導入の場合は、上記の情報を参照)

動作結果

  • 問題なし。更新前と同じように情報が表示された。
$ hg info
Repository: /Users/rabbit2go/projects/foo [hg root]
Base Hash: e91f351329d1c73501fd71d17fa61e3ebc08e116 [hg id -r0]
Revisions: 41 [hg tip --template "{rev}"]
Files: 50 [hg manifest | wc -l]
Cloned From: ssh://hg@bitbucket.org/rabbit2go/bar [hg paths default]

Readmeすら無い素っ気ないリポジトリだけど、修正版を出してくれている方に感謝したい。


関連

OutlookのドラフトメールをRubyで作成する

最近、Outlookの定形メールのドラフト版を、Rubyを使って半自動的に作っている。目的は下記の通り。

  • 置換作業の削減
    例えば、毎月送信するメールを作る場合、前月分のメールを参照して宛先 (to, cc, bcc)、件名、本文をコピペした後、該当する部分のみ書き換えることが多いだろう。でも、「4月」に送付したメールを「5月」に変更して流用する際に、『件名は「5月」なのに、本文は「4月」のままになっている』変更漏れメールを送信してしまった苦い経験を持つ人は多いはずだ(でしょ?)。こんな手作業に頼るからミスは減らないのであって、スクリプトを使って機械的に文言を置換えてしまえば、全ての「4月」は必ず「5月」に置き換わる。これなら置換えミスは決して起こり得ない。
  • 手作業の削減
    コピペすれば簡単に過去メールを流用できるというのは事実だけど、それはあくまでも最終手段であって、繰り返すことが分かっている将来の作業にまで手作業を予約してしまうのは感心できない。これからも繰り返すことが分かっているのなら、少しでも自動化を図り、人間の作業を介在させないようにすべきだろう。そもそも「内容にミスがないように気をつけてメールを作成する」という無駄なことにまで気を使ってしまうから、本来注意すべき重要な作業が疎かになってしまうのだ。気を使う作業は少しでも減らしておきたい。
  • 送信前の事前確認
    SMTPを使うメールの仕組みでは全てのメールを送ってしまうので、「送信前に一旦目視で確認しておきたい」という(我がままな)要求を満たすことが出来ない。その点、Outlookならドラフトメールの状態で保存されるので、不要なメールは送信せずに捨てて良いし、必要なら変更した形で送信することも出来るし、送信するタイミングを任意に選ぶことも出来る。

動作環境

スクリプト

下記に載っているサンプルをそのまま使えば良い。

require 'win32ole'
outlook = WIN32OLE.new('Outlook.Application')
message = outlook.CreateItem(0)
message.Subject = "Hey look a subject!"
message.Body = "Yes this is dog"
message.Recipients.Add 'dog@dog.com'
message.Recipients.Add 'cat@dog.com'
message.Attachments.Add('C:\Path\To\File.txt')
#Want to save as a draft?
message.Save
What's the easiest way to send a message through Outlook with Ruby? - Stack Overflow

この例では決め打ちのテキストだけど、実際には過去のメールをテンプレートとしてテキストファイルに保存しておき、該当ファイルから必要な箇所のみ(上記の例なら送信月毎に変わる「4月」の部分)ERBで文字列を書き換えてしまえば良い。これならバッチファイルを叩くだけで「宛先 (to, cc, bcc)、件名、本文」の全てが整ったドラフトメールが一瞬で出来上がってしまう。しかも件名も本文も全て「5月」に置き換わっているのだ。これなら申し分ない。後は内容を確認して送信ボタンを押すだけなのでなかなか便利だ。この便利さに慣れてしまうと、以前のような「コピペでメールを流用作成」なんて手間のかかる作業なんて面倒臭くて嫌になってしまう。

応用

手元の環境では同じWindows上でJenkinsを稼働させており、Jenkinsのトリガーを利用してドラフトメールを自動的に作成させている。これならTODOリストに「xxxへメールを送る」という項目すら作る必要が無いわけで、注意を払うのはドラフトメールのフォルダだけになる。下記はそうやって作るメールの一例だ。

  • 毎月(毎週)決まった日に送信するメール
  • サーバ上のファイルに変更が見つかった場合に送信するメール

なお、メール作成だけではなく、添付ファイルの自動保存やToDoの作成など、OutlookRubyで操作するための豊富なレシピが下記のサイトに載っている。これを使えば、作業効率は更に上がるだろう。

少しばかりの工夫で仕事は楽できるし、時間も手間も省くことが出来る。「時間が足りない」と愚痴をこぼす人に限って、退屈なコピペを延々繰り返しているのは残念なことだと思っている。



ToDoを片付けるだけの簡単なお仕事

新年度の事務作業は慌ただしい。前年度分のxxのデータをとりまとめるとか、新年度分のxxの書類を作成するとか、煩雑な事務作業が次々とやってくる。この情報が出揃うからこそ組織が成り立つとは言え、本当に必要なものなのか一度ゼロベースで考えてみた方が良いのではないだろうか?惰性で作り続けているものが多いのではないか?状況が変わったので必要性が薄れたものが多いのではないのか?と疑問に思うものの、そんな正論はもちろん無視されるので大人しく従うことにする。

とは言え、あまりにバカ正直に事務作業をこなすのではインテリジェンスの欠片もないので、賢く手を抜く方法をアレコレと考えるようになる。下記はその一例。

  • 書類はテンプレート化しておき、変更点だけを記入すれば済むように予め準備しておく。
  • 集計作業が容易に行えるような形で、データを集める。(例:手動コピペでの集計ではなく、マクロ一発での集計を選ぶ)
  • 作業手順を作成しておき、何も考えずに機械的に進められるようにする。
  • 次回の作業向けに、手順は随時更新し、問題箇所も残らず明記しておく

基本的にこの類の事務作業は全てRedmineのチケットに放り込んでいるので、新年度分の作業として必要なアクションは、前年度分のチケットをコピーする程度で良い。作業手順は全てwikiに記載されているし、成果物のファイルはリポジトリに入っているし、以前の作業にどのような問題が有ったのか過去チケットに記載されているし、メールで展開した内容はフォーラムに記載されているし、全く新規に考えねばならない作業はほとんど無い。頭を使わずに事務的に淡々と作業を進めれば成果物が出来上がり、しかもその結果は来年の同作業のベースとなるのだ。これは有り難い。いざとなれば、他の誰でも同じように作業出来そうだ。

今までの経験したことが無い新規の事務作業というものは実はあまり無くて、ほとんどの作業は過去の焼き直しで済ませることが出来るのではないかと思っているし、その方法論を突き詰めれば属人性に頼る仕事は殆ど無くすことが出来ると思う。個人の作業レベルでこれだけの工夫が出来るのなら、組織的に工夫を実行したら、きっと膨大な差異が生まれるはずだ。そんな事を考えつつ、「無印良品は、仕組みが9割」を読んだ。書籍の中では、新しい店の開店準備にて、ベテラン店長達のアドバイスが異なるせいで混乱に陥る売り場の例が紹介されている。

その時、他店の店長が応援に駆けつけました。
そして売り場を一目見るなり、「これじゃあダメだよ、無印らしさが出てない」と、いきなり商品の並び替えを始めたのです。
新しい店の店長は戸惑っていましたが、ベテラン店長に物申すわけにもいかず、結局スタッフ総出で並び替えました。
ようやく並び替えが終わったころ、今度は別の店長がやってきました。そして、「ここはこうしたほうがいい」と、直しはじめました。
そのような調子で、応援に来た店長がそれぞれ売り場を変えてしまうので、夜の一二時を回っても、まだ作業は終わりませんーー。

こんな場面を目の前にして「売り場のあるべき論」を主張するのは、仕事の何たるかを理解していない証拠だろう。むしろ、一つ上のマクロな視点で、本質的な問題の存在に気づくべきなのだ。実際、著者の方は、この光景を目撃したことをきっかけに、社内の業務標準の作成に取り掛かっている。見るべき人が見れば問題の所在は明らかなのだろう。目の前に置かれた作業の山に取り組むことが仕事ではなく、その作業のやり方を考えることこそが仕事の本質ではないかと思っている。

その光景を見ながら、私は「まずいな。このままでは無印良品の未来はないんじゃないか」と感じていました。
悪い予感は的中しました。



関連

仕様の表現力で発注者の力量が分かってしまう

「この位の仕様で上手く作っておいて」とひと言伝えておけば、望み通りのシステム一式が出来上がって来ると嬉しい。これなら細かい点まであれこれと指示する必要も無く手間が省けるし、期待通りのモノが期待通りの納期、品質で出来上がってくれば万々歳だ。

しかしながら、世の中、そんな「あうんの呼吸」が通じる関係というものはなかなか無い。明文化できないノウハウは暗黙知として開発者の頭の中に入ったままなので、頼れる人がいなくなってしまうと、誰も作業を代行できない。長年に渡って作業を担当してくれていたベテラン開発者も「寄る年波には勝てず体調を崩して休み」が続くと、途端に問題が噴出してしまう。

だからこそ、設計資料は必須だ。組織として開発をすすめる以上、人が入れ替わっても作業を継続出来るように設計情報の伝授は不可欠なのだ。何を何処まで作成するのか開発スコープを定め、設計の意図や方針検討時の代替案などソースコードには表現できない情報を記載し、その内容に関してステークホルダー間の合意を得ることは、リスク満載な開発における重要な保険とも言える。

異なる文化背景、言葉を持つ人たちが関わるプロジェクトでは、仕様書を通じて認識合わせを行いチームの意思統一を図る訳だが、幸か不幸か日本国内の場合、そんな多様性とは無縁の環境で働く文化が続いてしまったので、あうんの呼吸でなし崩し的に進める進め方が根づいてしまった。仕様で明文化されている事実よりも、仕様の行間を読む能力が重宝がられるのは、そんな文化の象徴の一つかも知れない。

グローバル化と言うと、英語を話せるようになるとか、海外市場へ進出するとか、何か別次元の取り組みを考えてしまうけど、実は全然そんなことはなくて、まずは自分達の要求を他者に分からせる為の表現力が第一に必要では無いだろうか。英語はその要求を異なる形式に変換する為のツールに過ぎないし、海外市場はその適用先の一つに過ぎないはずだ。幾ら英語を話せるようになっても、肝心の中身が乏しいのならコミュニケーションは成立しないのだ。

そんな事をつらつらと考えていたら、他の業界でも同様な問題が有るとのニュースを見かけた。自分が必要とするものを言葉で定義出来ず、責任を外部に丸投げしてしまうのは、実はIT業界に限った話ではないらしい。

この条文を正確に英語に訳して本国に伝えたところ、「無限の責任を要求されているので、これでは見積り金額を算出できない」「この鉄道会社は自社の技術的要求を数字や文章で表現する能力を持っていない。鉄道先進国である日本にこんなレベルの低い鉄道会社があるとは信じられない」といった返答があった。

日本の鉄道に「海外製」が増えない根本原因 | 海外 | 東洋経済オンライン | 経済ニュースの新基準



Let's EncryptでサーバをHTTPS接続に対応させた

Amazon EC2で稼働させているサーバを、Let's Encryptを使ってHTTPS接続に対応させた。高いお金を払わなくても、無料でHTTPS接続が可能になるのだから便利な時代だ。

動作環境

導入

下記の手順に従って導入すれば良く、問題は何も発生しなかった。

sudo curl https://dl.eff.org/certbot-auto -o /usr/bin/certbot-auto
sudo chmod 700 /usr/bin/certbot-auto
certbot-auto certonly --webroot -w /var/www/html -d [server] --email [mail_address] --debug

Apacheの設定/etc/httpd/conf.d/ssl.confに追記した。

SSLCertificateFile /etc/letsencrypt/live/[server]/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/[server]/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/[server]/chain.pem

動作確認

動作確認を行ったところ、下記の問題が判明した。

贅沢を言うつもりはないけれど、iOSから接続できないのは困る。調べたところ、Safariが要求する接続条件を満たしていないのが原因と判明した。

実は,この状態でiOSSafari等からサーバにアクセスしても,証明書はすぐには
受け入れてもらえません. これは,SSLの設定がiOSの信用条件を満たしていない
ことに起因するようです.

Let's Encryptを使って,サーバとiOSでhttpsで通信できるようにする

どのような接続方法までを許容し、何を拒否するのか?というのはサイト運営のポリシーに関わる内容であり、一律的な基準があるわけでも無いので判断が難しい。こちらは片手間にサーバの管理を行っているだけであり、常に最新動向を把握している専任の担当者ではないのだ。どこまでを許容して良いのか判断が出来ないのだ。

しかし、こんな悩める管理者に助言してくれるという、有り難いサービスがある。今回はSSL Server Testというサービスを使って暗号化の検証を行った。具体的には、サイトのURLを入力するだけで良く、後は自動的にHTTPS接続して一連の暗号化の設定状況を確認してくれる。その結果は"A+"のような指標で示されるので、判断の根拠として活用できそうだ。

This free online service performs a deep analysis of the configuration of any SSL web server on the public Internet.

https://www.ssllabs.com/ssltest/analyze.html

試行錯誤しつつ、Apacheの設定は下記の様に変更した。

# セキュリティに問題があるプロトコルを除外する
SSLProtocol all -SSLv2 -SSLv3
SSLProxyProtocol all -SSLv2 -SSLv3
# 暗号化方法を限定する
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!EXP:!LOW:!aNULL:!eNULL:!ADH:!DSS:!MD5:!PSK:!SRP:!RC4:!3DES
# サーバ側の暗号化方法を優先する
SSLHonorCipherOrder On
# 常時HTTPS通信を行う
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains" 

結論

この状態で再度上記のサービスを使って確認したところ、今度は"A+"の評価が付いた。Chromeはもちろん、Safariでも正常に接続できることを確認した。昔のウェブブラウザや古いJavaではアクセス出来ないようだが、セキュリティ上の問題を抱えるよりは良いので、問題なしと判断した。

教訓

昔はSSL接続が出来ていればOKと言うのどかな時代だったけど、最近はセキュリティホールが見つかり暗号化方法として非推奨扱いに変わったり、新しい暗号化の方法が加わったりして変化が激しい(そもそもSSL自体が過去のものなのだ)。上記に記載した設定も、きっと1年後には時代遅れの設定となり、"A+"の判定は得られないのだろう。兼務管理者には辛い時代ではあるけれど、無料で支援してくれるツールを上手く活用しつつ、時代の変化にキャッチアップして行きたいと思っている。

多くの人は、「SSLが使われているサインだ」と理解しているはずだ。ところが実際は異なる。この場合のほとんどはSSLの後継のプロトコルTLS」を使っている。なぜか? SSLが「使用禁止」になったためだ。安全な通信手段の代名詞は今後、TLSとなる。

SSLはなぜ「もう古い」なの? | 日経 xTECH(クロステック)

Docker for Macを試す

時代のトレンドはDockerらしいので、Docker for Mac(正式版)を試したみた。

DockerはmacOSWindowsの環境で簡単にDocker環境を構築できるツール「Docker for Mac」「Docker for Windows」の正式版をリリースしたと発表しました。

Docker for Mac/Windowsが正式版としてリリース - Publickey

以前のバージョンなら仮想マシンとしてVirtualBoxをインストールする必要があり、重いソフトを起動させるのは(気分的にも)重いものが有ったのだけど、正式版ではネイティブな仮想化機能がサポートされたので、その課題が解消されている。

Docker for Mac and Windowsは、それぞれのOSのネイティブアプリケーションの形式で提供され、VirtualBoxの代わりにそれぞれのOSに組み込まれている仮想化機能、WindowsではHyper-VMacOSではxhyveを用いることで、より高速で安定した動作が実現できると説明されています。

MacOSとWindowsのネイティブ仮想化を用いたDocker純正ツール「Docker for Mac/Windows」登場、VirtualBoxは不要に - Publickey

動作環境

インストール

下記からダウンロードしたDocker.dmgを使った。(Docker.appをアプリケーションフォルダに入れるだけ)

Docker for Mac is our newest offering for the Mac. It runs as a native Mac application and uses xhyve to virtualize the Docker Engine environment and Linux kernel-specific features for the Docker daemon.

https://docs.docker.com/engine/installation/mac/

なお、Homebrew経由でもインストール出来るが、複数のアプリケーションを組み合わせる必要も無く、またDocker.app自身に更新機能が備わっているので、上記のインストーラを使うのが手っ取り早いと思われる。

brew cask install docker

実行

上記でインストールしたDocker.appを起動させれば直ぐに使える状態になる。(動作状態はメニューバーのアイコンに表示される)


動作確認

まずは動作確認用としてechoコマンドで"Hello world"を実行させた。具体的には下記のコマンドを叩くだけで良い。

$ docker run ubuntu /bin/echo 'Hello world'

https://docs.docker.com/engine/tutorials/dockerizing/

初回のみイメージのダウンロードが行われるので少しだけ時間を要するが、"Hello world"が表示された。2回目以降の実行では、ローカルのイメージを使うので、もちろん待ち時間は発生しない。

$ docker run ubuntu /bin/echo 'Hello world'
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6bbedd9b76a4: Pull complete 
fc19d60a83f1: Pull complete 
de413bb911fd: Pull complete 
2879a7ad3144: Pull complete 
668604fde02e: Pull complete 
Digest: sha256:2d44ae143feeb36f4c898d32ed2ab2dffeb3a573d2d8928646dfc9cb7deb1315
Status: Downloaded newer image for ubuntu:latest
Hello world

次にシェルを起動させた。(引数のオプションに関する説明は下記のサイトを参照)

$ docker run -t -i ubuntu /bin/bash

https://docs.docker.com/engine/tutorials/dockerizing/
$ docker run -t -i ubuntu /bin/bash
root@2cba2a194e4f:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

普通のLinuxのフォルダ構成になっていることを確認した。

所感

導入の容易さはもちろんのこと、仮想環境の起動の早さは驚異的だと思う。上記の一連の作業を行うだけなら(ファイルのダウンロードに要する時間は別として)数分程度で出来てしまう。昔、Mac用の仮想環境としてVMware Fusionを導入してWindowsLinuxを使っていたことがあるけれど、それに比較しても手間は圧倒的に少ない。今後は仮想環境をもっと気軽に活用できる気がする。


PythonのSSLHandshakeErrorにはまる

AWS LambdaとAPI Gatewayに作成したAPIを、Pythonから呼び出す際にはまったので覚え書として残す。

動作環境

クライアント

Pythonで下記のクライアントを作成した。事前にcurlを使って動作確認は出来ていたので、サーバ側のAPI処理は問題ない。

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import httplib2
import json

myheaders = {
  "x-api-key" : "xxx",
  "Content-type" : "application/json",
  "Accept" : "application/json"
}
mybody = {
  "value" : "123"
}
mybody = json.dumps(mybody)
 
url = "https://xxxx.execute-api.ap-northeast-1.amazonaws.com/prod/xxx"
ht = httplib2.Http()
res, content = ht.request(url, "POST", mybody, headers = myheaders)

print res.status
print content

問題

ところが実際に動かしてみると、下記のSSLエラーが発生してしまう。

$ ./post_data_error.py 
Traceback (most recent call last):
  File "./post_data_error.py", line 21, in <module>
    res, content = ht.request(url, "POST", mybody, headers = myheaders)
  File "/usr/local/lib/python2.7/site-packages/httplib2/__init__.py", line 1609, in request
    (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
  File "/usr/local/lib/python2.7/site-packages/httplib2/__init__.py", line 1351, in _request
    (response, content) = self._conn_request(conn, request_uri, method, body, headers)
  File "/usr/local/lib/python2.7/site-packages/httplib2/__init__.py", line 1272, in _conn_request
    conn.connect()
  File "/usr/local/lib/python2.7/site-packages/httplib2/__init__.py", line 1059, in connect
    raise SSLHandshakeError(e)
httplib2.SSLHandshakeError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:590)

SSLHandshakeErrorとは一体何だろう?SSLが絡むエラーなので(良く有る様に)証明書が不足しているのかと思って調べてみたが、そうでは無いらしい。調べた限りでは、SSLのコネクション時に必要な実装が含まれていないらしいと分かった。

The problem is the lack of the Server Name Indication (SNI) extension in the TLS handshake, which the twitrss.me site apparently requires:
...
I also looked at packet dumps to verify that SNI was missing when attempting connection using Python.

python - SSLv3 alert handshake failure with urllib2 - Stack Overflow

対策

考えてみれば、たまたま手元に有ったPythonスクリプトを流用して作ったクライアントなので、httplib2を使う必然性は実は全くない。実際、httplib2ではなくurllib2を使う形に書き換えたら(変更箇所は少なく容易)、urllib2でも上記のエラーが発生することなく正常に処理が完了した。

import urllib
import urllib2
...
req = urllib2.Request(url, headers=myheaders)
req.add_data(mybody)
res = urllib2.urlopen(req)
print res.code,
print res.read()

httplib2とurllib/urllib2は、使い勝手に差が有る程度と認識していたのだが、実は内部実装の違いに起因する機能面での差異もあると初めて知った。

urllib/urllib2 is built on top of httplib. It offers more features than writing to httplib directly.
however, httplib gives you finer control over the underlying connections.

http - Python urllib vs httplib? - Stack Overflow

結論

もっとも、時代は更に先を進んでいて、今はurllib2ではなく、Requestsを使うのがオススメらしい。(ちなみに"NON-GMO"とは「遺伝子組み換え食品を使っていない」という意味なので、不純物が含まれておらず開発者に優しいとのこと...)

より高いレベルの http クライアントインターフェイスとしては、 Requests package がお奨めです。

20.6. urllib2 — URL を開くための拡張可能なライブラリ — Python 2.7.14 ドキュメント

Requests is the only Non-GMO HTTP library for Python, safe for human consumption

Requests: HTTP for Humans™ — Requests 2.21.0 documentation

結局、今回のスクリプトは下記の様に書き換えておいた。(こちらも変更箇所は少なく移行は容易。もちろんSSLエラーは発生しない)

import requests
...
res = requests.post(url, data=mybody, headers=myheaders)
print res.status_code
print res.json()

受信したデータをjsonにまでデコードしてくれるのは、なるほど今時のライブラリだと思う。