GitHub/BitbucketからAccess Tokenを使ってdeployする

あるいはサーバ上で private package を bundle(npm) install したい。

課題

今回の課題はこれ。

private な repos をサーバ上で clone したい

もう少し詳しく書くと直接の git clone もそうだし、bundle install や npm install で private repos から package を install したいが、もちろん

秘密情報は repos に入れたくない

というものである。

※ Access Tokenが使えれば他のサービスでもサーバでもよい。

解決方法

  1. 環境変数と.netrcを使う
  2. 環境変数と動的なURL書き換えを使う

1. 環境変数と.netrcを使う

この方法を知ったきっかけはこれ。

timshadel/heroku-buildpack-github-netrc: Heroku buildpack to access private Github repos over HTTPS without storing user/pass in your files.

やってることは、

  • 環境変数にトークンを保存
  • build 時に環境変数から ~/.netrc に保存し、git の機能で ~/.netrc の情報で認証を通す

これで確かにリポジトリに認証情報を残さずに private repos から clone できる。

もちろん token の権限設定は必要最小限にしておこう。

サーバ上の環境変数設定を簡単に行える環境ならこれが楽だと思う。

2. 環境変数と動的なURL書き換えを使う

Capistrano で clone することで deploy する際には上とは異なる方法を利用することにした。

Easier builds and deployments using Git over HTTPS and OAuth

の情報をもとに Capistrano を叩く環境に変数でトークンを保存したうえで URL を

set :repository, "https://#{fetch(:github_auth_token)}:x-oauth-basic@github.com/{user}/{repos}"

と、cap を叩く時に書き換える。Gemfile や package.json がないならこの方法も使える。

※ Gemfile や package.json がある場合は頑張って上の buildpack と同じようなことをしてあげることになると思う。

手元の環境からも deploy したい場合は dotenv のようなものを使うか、.netrc に情報を保存するのもアリ。Capistrano 側のコードはこんな感じになる。

set :github_auth_token, ENV['GITHUB_AUTH_TOKEN'] || Netrc.read['github.com']['login']

..netrc を読み書きできる便利 gem はこれ。

heroku/netrc: Reads and writes netrc files.

なぜ他の方法ではないのか?

ここから先はちなみに情報。

Git 自体が対応している認証情報の保存方法がいくつかある。最近では自分で手で作業する場合はこのうちのいずれかを利用しているケースが多いと思う。

Git - 認証情報の保存

によると

  • cache (15分だけメモリに保存)
  • store (永続的にファイルに保存)
  • osxkeychain (macのkeychainアプリにパスワードを保存)
  • wincred (Windowsのkeychainのようなもの)

の4つに対応している。

ただしこれらは、人間がパスワードを入力しその認証情報を保存する、という流れを想定しており、Coutinuous Deployment で利用するにはイマイチである。

Git でパスワードを入力せずに push / pull するということでロートルがすぐに思いつくのはパスフレーズなし鍵認証の ssh を使う方法である1が、今回行おうとしているのは

サーバ上で git clone や bundle install や npm install したい

ということであり、サーバ上に余計なものは入れたくないし、まして Heroku などの PaaS 上では採用できない方法である。(deploy用の公開鍵を保存する機能はあっても秘密鍵を保存する機能が存在しなかったりする。)

なぜ.netrcで可能なのか?

最後になるけど、なんか普通に .netrc って言ってるけど、そもそもこれ何?って話。

  • git は http での転送の際に cURL を利用している
  • cURL は .netrc に対応している

..netrc って古の ftp ですよね?ってぼくは思っていたのですが、cURL 力不足ということのようでした。(実は cURL あまり好きじゃなかった)

cf.

Man page of NETRC

  1. なぜなら Git で http での push / pull が使いものになるのは 1.6.6 で登場した Smart HTTP が使えるようになる 2009年以降の話であり、それまでは CVS や Subversion でもおなじみだった ssh と組み合わせる方法の方がポピュラーだったのだ。 

ノーフレームワークレガシーPHPから始めるユニットテスト

巨大なアクセスを抱える大規模サイトは経験ないので分かりません。

あと、本当にレガシーな PHP しか考えてません。とにかくイマドキのフレームワークを使ってください。こんなこと気にする必要ないんでしょ?

設定

できるだけphp.iniに依存しない

  • まず、PHP のコードで設定できるものは PHP のコードで設定した方がよい
  • そうすれば複数サイトの開発のための切り替えも比較的スムーズに行える
  • extension など php.ini にしか書けないものは php.ini に書けばよい

これを踏まえたうえで、自分の場合はプロジェクトごとに .htaccess および php.ini を同時に生成する setup 用のスクリプトを用意している。.htaccess に書く rewrite の設定などもここで展開するようにしている。で、このスクリプトをリポジトリに入れてある。

必要に応じて RewriteBase も設定できるようにしておけば、ある程度は開発環境と本番環境でパスが違うといった状態でも開発可能になる。1

こうしてプロジェクト、環境ごとの違いを setup スクリプトに与えるパラメータだけで吸収する。これのおかげで mod_php か cli かを気にせずテストできるようにしている。

今回 Jenkins を入れる際にもこの setup スクリプトのおかげで実は設定自体はすぐに済んだ。

PHPでPHPを設定する

この話はすでに PHP の設定って PHP で書いた方がよくない? に書いてある。

PHP 5.3 からは php.ini 内で path や host でセクション指定して設定を分けることができるんだけど、システムの php.ini ってプロジェクトのリポジトリには馴染まないので、別メンテになってしまってよろしくない。

iniファイル + auto_prepend_file

プロジェクトごとの ini ファイルを用意するとテストは

php -c inifile -f testsuite.php

のように実行することができる。

この inifile の中で

include_path =
auto_prepend_file =

を指定してやれば

  • 共通のライブラリのパスを設定
  • 設定用の PHP の読み込み

を行える。

実際には自分の場合は

ROOT
  .htaccess
  _test/
    .htaccess
    php.my.ini
    testing.php
  app/
  config.php
  php.my.ini
  ...

みたいな感じになってて、testing.php から config.php を読み込むようにしている。

  • 普段のアプリの実行は config.php のみで
  • テストのときはテスト用のライブラリの読み込みとか準備を testing.php で行いつつ config.php も読み込む

という流れにしてある。

まぁ testing.php は spec_helper.rb みたいなもん(ひどい)

ユニットの切り出し方

『レガシーコード改善ガイド』読め

まぁこれだけだとなんなので実際いちばんよくやったのだけ紹介。

とりあえずclassに

もう

<?php
class Klass {
  function run() {
    最初からあったコード全部
  }
}

if ( realpath( $_SERVER['SCRIPT_FILENAME'] ) == __FILE__ ) {
  $app = new Klass();
  $app->run();
}

こんなんでもよい。

いきなり順次実行さえしなければあとで必要なときに料理できる。

まさかのグローバル変数はstaticなclassに

PHP 3 の時代は require してる他のファイルのグローバル変数を前提にしているものもままあったけど、さすがに今はない…よね?

どうしてもって場合は static な class 用意してメソッド経由でメンバ変数読み書きさせればマシかな。そうすりゃこの class にログの仕組み仕込めば追跡できるし。

確認したい部分をメソッドに

メソッドに切り出す段階でグローバル変数は global 宣言が必要になるので、ついでにメンバ変数にしちゃう。

で、切り出したメソッドはできるだけシンプルな入出力を提供する形にする。ダメならメンバ変数の状態でたぶん何かが確認できるので、それをテストする感じになると思う。

重たいコンストラクタはバラす

DI だろ DI (愛だろ、愛の感じで) とは思うけど、すぐにできない場合は

class Klass {
  function __construct() {
    ...
    $this->_init_foo( $foo );
    $this->_init_bar( $bar );
    ...
  }

  function _init_foo( $foo ) {
  }

  function _init_bar( $bar ) {
  }
}

こんなんでよい。で、テストのときは

class Klass_Test extends UnitTestCase {  // SimpleTest の場合
  function setUp() {
    $this->obj = new Klass();
    $this->obj->_init_foo( $config );
  }
}

みたいな感じでテストに都合のいい設定やオブジェクトを突っ込む。

ちなみにテスト用 class の名前を

Klass_Test

にしたのは理由があって、Klass を継承してテスト用に変更するときに

TestingKlass

という名前を使うから。これは『レガシーコード改善ガイド』の中で紹介されている命名ルールに従っている。

スプラウトクラス

レガシーコード改善ガイドに出てくるテクニックのうち、いちばん好きなもの。

でかくて複雑なメソッドを丸ごとクラスに追い出してそのクラスの中でバラしていく方法。

なんだけど、

とにかくまっさらでテストしやすいclassを作る

ためにも使える。

本買って読んで!

ライブラリにする

テストしやすさのためには要するに独立していることが大事なので、当然の流れとしてライブラリ化というのは出てくると思う。

以下脱線。

気をつけないとこのライブラリこそが癌になりかねない。このソーシャルコーディングの時代に自社で閉じたライブラリを整備することは十分な切り分けを考えた上でやらないといけないと思う。

個人的には PHP は LL の中では使いやすい大きさのちょうどよいライブラリがとても少ないと思う2ので、勢いこうしたライブラリの整備に話は進みやすいと考えている。本当にレガシーな環境(PHP 4とか)であれば仕方ないけど、そうでないならオリジナルライブラリはできるだけ最小限に留めた方がよいと思う。

  1. パスに関しては本当は PHP 5.4.0 以降の built-in HTTP サーバなど、他の LL のようなサクッと起動できる開発用のサーバがあれば話は簡単なんだけど。 

  2. なぜかみんなフレームワークの中に収めようとする 

git log --color | less

git ってメッセージを color で出力できてステキなんだけど、パイプで渡してるかどうかを見てるので、ページャで読もうとすると途端にモノクロになってツレナイ感じ。

diff に関しては以前colordiff を挟む方法で color 化したことがあるんだけど、log も color で出ないかなと思ったらまんま

--color

っていうオプションがあった。なるほど。

実際には log -p(つまり diff)を color 表示したいわけじゃなくて commit 間の区切りが分かりにくいのをなんとかしたかっただけなんだけどね。svn diff は AA みたいな table 形式で表示されるので commit と commit を容易に区別できたんだけど、git log は区切りが分かりにくいなぁってずっと思ってた。

とりあえず color にするだけでずいぶん見やすくなったのでこれでいくことにする1

  1. 本当は color にしなくて見やすくする方法があればなおよいかなとは思うんだけど。 

ドラえもんリニューアル

変と言えば変だけど、なんか思ったほど違和感がないなぁ。

というかアフレコだけじゃなくて完全に新規に制作してるのか。色も違うし、てことはたぶん原画から起こし直してるんだろうなぁ。すげー。初回は道具てんこ盛り。

というわけでようやく Another html-lint レベルまで

重い腰をどうにか上げて説教講座の改修に実際に着手したのが去年の夏。すでに1年近い歳月が過ぎているが、ようやく html-lint を掛けながらの細部のチェックをできる段階にきた。

もちろんこれは内容のチェックを含まないものだが

とりあえず

  • xrea にすべてのコンテンツを置く
  • コンテンツを CVS で管理できるようにする

のが先決。これが済んだら内容も地道に見直しを加えていく。まぁ、この段階はそれこそ気長な話なので焦る必要はないし、気楽なもんだ。

早くそこまで行きたい。

CPAN

実は未だに CPAN モジュールを通じて CPAN からモジュールをインストールする方法がよく分からない。が、自作のスクリプトに Jcode.pm 依存のものがあるのでこれを cygwin で動かしたくてついにやってみることに。(FreeBSD や Debian はこうしたものもパッケージで入れられるので。)

やってみるとこれが簡単。さすがに cygwin は環境が整っている。(ちなみに cmd + ActivePerl でもやってみたが、make がうまく動かず速攻ステ決定。)

ということで Another html-lint も ActivePerl ではなく cygwin 版の Perl で動かすことにしてしまう。以前のように Perl が動けば満足していた頃と違い、最近はやはり各種コマンド一揃えでないと不便で仕方がないので、スピードさえ我慢できれば ActivePerl 要らないんだよな。。。

Jcode.pm には win_install.pl があった。

Another html-ling が動いたので気をよくしたが、すぐに重さが気になった。やっぱ cygwin じゃ遅いか。そこでアーカイブの中をチェックしたら Jcode.pm には win_install.pl なんて便利なのがあんじゃん。これを実行したらあっという間にインストール終了。

久しぶりに local でまともに動く Another html-lint をセットアップした。やっぱ HTML をいじるときにはこれがないと。

鼻炎の薬を飲む

どうも調子が悪いので薬を飲んだら楽になった。花粉症というほどはっきりしたものではないのだが、春になるとやはりどうも喉鼻の調子は悪くなる。

About

例によって個人のなんちゃらです