TravisCI を色々なレポジトリに導入して遊んでいて、 GitHubにテストでpushする必要が出てきたので色々やってみたことについて。
- GitHubへpushするのに必要なもの
- Travisのコマンドラインツールを用意する
- ファイルを暗号化
- 文字列を暗号化
- encrypt/encrypt-file共通の注意
- ファイルをencryptを使って暗号化
- まとめ
GitHubへpushするのに必要なもの
普段手元のPC等からは公開鍵認証を使っています。
ですが、勿論Travisにレポジトリを送った際に作られるテスト環境には 鍵が置いてないので何らかの方法で認証を行う必要があります。
また、別の方法として、Access Tokenを作って それを用いてアクセスする方法もあります。
Easier builds and deployments using Git over HTTPS and OAuth
これらいずれの場合もTravis側に鍵を送ったりTokenを知らせる必要があるわけですが、 そのままファイルをレポジトリに追加したりTokenを.travis.ymlに書いたりすると、 GitHubの公開レポジトリの場合には大変なことになります。
そこでTravisでは文字列やファイルを暗号化して送れる様なシステムが用意されています。
Travisのコマンドラインツールを用意する
暗号化を行うにあたってTravisのコマンドラインツールが必要になるので インストールする必要がります。
Ruby1.9.3以上が必要で、インストールにはgem
コマンドを使って
$ gem install travis
とすればOK。
インストールが済んだら自分のTravisアカウントと手元のマシンのtravisをつなげるために
$ travis login --auto
というコマンドを行います。
--auto
を使うと
.netrcで設定してあるアクセストークンを探したり、
Macの場合にはKeyChainに認証の情報があればそれを用いて自動でセットアップしてくれます。
--auto
をつけなかったり、それらで認証出来ない場合には、
GitHubのユーザー名やらパスワードやらを聞かれて、
それを使ってアクセストークンを作ってTravisと繋げられる様にします。
最初に実行した時に
Shell completion not installed. Would you like to like to install it now? |y|
みたいな事も聞かれて、y
と返事すると~/.bashrcなんかに
# added by travis gem
[ -f $HOME/.travis/travis.sh ] && source $HOME/.travis/travis.sh
といった行が加えられると同時に
~/.travis/travis.shも作成されます。
この設定はtravis
コマンドにおける補完の設定です。
ファイルを暗号化
公開鍵認証を使ってGitHubにTravisのジョブからアクセスする方法についてです。 こちらは今年の8月にtravis 1.7.0から追加された方法です。
鍵の用意
まず、専用の鍵を作ります。
$ ssh-keygen -t rsa -C "for test deploy"
パスフレーズ無しで、出力先は適当に$HOME/.travis_rsaとでもして作成します。
(実際に打ち込むときは$HOME
とか~
等は解釈してくれないので/Users/…とベタ書きする必要があります。
もしくは$HOME
にいってファイル名だけでも$HOME
に出来ます。)
パスフレーズを入れてしまうとTravisのジョブ内で止まってしまうので無しで。 どうしても入れたい場合には下に書く文字列を暗号化する方法で Travisに渡してあげて、さらにssh使用時にきちんと渡してあげる必要があります。
これで$HOME/.travis_rsaと$HOME/.travis_rsa.pubができているはずです。
秘密鍵の暗号化
作成できたら秘密鍵の方を暗号化:
$ travis encrypt-file -r rcmdnk/travis-test .travis_rsa
encrypting .travis_rsa for rcmdnk/travis-test
storing result as .travis_rsa.enc
storing secure env variables for decryption
Please add the following to your build scirpt (before_install stage in your .travis.yml, for instance):
openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d
Pro Tip: You can add it automatically by running with --add.
Make sure to add .travis_rsa.enc to the git repository.
Make sure not to add .travis_rsa to the git repository.
Commit all changes to your .travis.yml.
$
コマンドはtravis encrypt-file
を使います。
追記: 2016/04/13
この際、
repository not known to https://api.travis-ci.org/: rcmdnk/travis-test
の様なエラーが出たら、
$ travis sync
をしてみてください。
loginを行った後に作ったレポジトリとかだと出る?っぽい感じですが、
login時にその辺の情報をを集めてるのか、
sync
をするとその辺の情報の最収集をしてくれるようです。
追記ここまで
-r
でTravis CIに送るレポジトリを指定します。
(Travis CIのジョブの中でアクセスしたいレポジトリではない)
このレポジトリの中で使った場合のみTravis CI側で解読されるようになります。
最後に暗号化したいファイルを与えます。
上手く行けば.travis_rsa.encというファイルが出来てるはずです。
ここで出てきている
openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d
の行は書いてあるとおり後で.travis.ymlに追加するのでメモっておくなりしておきます。
公開鍵をGitHubへ登録
これで準備が出来たので、実際にレポジトリに情報を追加したりしていきます。
まず、作成した公開鍵.travis_rsa.pubをGitHubに登録します。
セッティング項目にある SSH Keys へ登録しても使えますが、これだと全レポジトリにアクセスできる物になってしまいます。
Travisの方で不具合があると漏れてしまうので、テスト用の鍵は 使うレポジトリ専用にするため レポジトリにある Settings Deploy Keys から登録しておきます。
この際、push
したいので、Add deploy key
を押して
鍵を登録する際、一番下にある
Allow write accessにチェックを入れておきます。
このレポジトリは上でTravisに送るレポジトリとして指定したものと違うものでも構いません。
e.g.) travis-testというレポジトリをTravisと連携してpush時にテストジョブを走らせるようにしておいて、
そのテストジョブの中でoctogray_testというレポジトリにpushする場合は、
travis encrypt-file
ではtravis-testレポジトリを指定して行う。
Travisに送るレポジトリでの設定
公開鍵の登録が出来たら実際に使うレポジトリをcloneするなり用意します。
まずは上で作成した暗号化されたファイルを追加 1:
$ git clone travis-test
$ cd travis-test
$ cp ~/.travis_rsa.enc
$ git add .travis_rsa.enc
次に.travis.ymlの中に、上の暗号化時に出ている表示に在る通り
before_install:
- openssl aes-256-cbc -K $encrypted_ee14946e6582_key -iv $encrypted_ee14946e6582_iv -in .travis_rsa.enc -out .travis_rsa -d
- cp .travis_rsa ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
といった感じに、before_install
のブロックに暗号解読のコマンドを入れます。
さらに、解読して出来た.travis_rsaをデフォルトキーとして~/.ssh/id_rsaへコピーします。
複数の鍵を使いたい場合にはその度、~/.ssh/id_rsaへ順にコピーするか、 ~/.ssh/configを書き換えるコマンドを用意する必要があります。
追記: 2014/11/3
git clone [email protected]:/rcmdnk/travis-test
みたいな、ssh([email protected]:...
)を使ったgitのcloneを行いたい場合には
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
もbefore_install
ブロックに加えておく必要があります。
StrictHostKeyChecking no
を指定しておかないと、
github.com
はデータベース(~/.ssh/known_hosts)に無いホストなので加えて良いか
聞かれるプロンプトが出てくるわけですが、ここで止まってしまいます。
また、OS Xではこの部分が外からsshでつないだりして作業を行ったりする場合には
機能せずにエラーを出します。
Travisでの作業はこの様な状態にあたるらしく、
StrictHostKeyChecking no
しないでknown_hostsに無いホストへ
ssh
で接続しようとすると
remote: ssh_askpass: exec(/usr/libexec/ssh-askpass): No such file or directory
というエラーが出てしまいます 2。
ということで、git clone
を[email protected]:…で使いたい場合には
StrictHostKeyChecking no
の設定が必須です。
(他にもssh
的な作業をしたいなら同じくそれらのホストに対して必須です。)
もし、鍵をid_rsa
以外の名前で置いておきたい場合には
- echo -e "Host github.com\n\tStrictHostKeyChecking no\nIdentityFile ~/.ssh/travis_rsa\n" >> ~/.ssh/config
みたくgithub.com
に対する鍵を指定しておけばOK。
追記ここまで
また、~/.gitconfigなんかも当然無いので、gitのコミッター名等をenv: global:
に
env:
global:
- GIT_COMMITTER_NAME=rcmdnk
- [email protected]
- GIT_AUTHOR_NAME=rcmdnk
- [email protected]
の様に加えておきます。
これで、
script:
- git checkout -b test
- git branch
- touch testfile
- echo "test1" >> testfile
- rm -f .travis.yml
- git add -A
- git commit -a -m "test1 commit"
- git push -f [email protected]:rcmdnk/travis-test test
と、sshを使ったレポジトリ指定(git@…)でのpushを書いておけばTravis CIの中からtravis-testに向けてpushが出来ます。
encrypt-fileを使うときの注意点
絶対に秘密鍵そのものをレポジトリに追加しない
上では$HOMEに鍵を作って後でコピーして、みたいなことをしてますが、 これは間違って秘密鍵をレポジトリに追加しないようにするためです。
encrypt-file
コマンドはレポジトリ内で行うと
-r
が省略できて自動的に今いるレポジトリで暗号化を行うようになりますが、
間違ってgit add -A
とかすると秘密鍵も登録されてしまうので
鍵ファイルの扱いは他の場所で行った方が良いかな、と。
encrypt-fileは実行するたびに新しい暗号化を行う
encrypt-file
コマンドを行うと、
同じ場所、同じファイルに対しては
出てくるコマンド(openssl...
)は全て同じですが、
作成される*.enc
ファイルは新しくなっています。
なので、コマンドを打ったら必ず*.enc
ファイルも再びcommit/pushする
必要があります。
1つのディレクトリで1つのファイルしか使えない?
きちんとしたDocumentが見つからないのでやってみて、ですが、
encrypt-file
に関しては、
-K $encrypted_ee14946e6582_key
のパスワードの部分は
同じディレクトリで行う限り全て同じ文字列が出てきます。
これはファイルを変えても同様。
これが、他のディレクトリ(行いたいレポジトリの内外問わず)で行うと 違う文字列になります。
これでちょっと試してみたところ、2つのファイルを暗号化したい時に、 同じディレクトリでそれぞれ暗号化してTravis CIに送ると上手く行きませんでした。 最初に暗号化した方のコマンドが解読できない、と言ったエラーを出します。
一方、それぞれ違うディレクトリに持って行って暗号化すると 両方共きちんと解読してファイルを元に戻してくれました。
その他
下にある共通の注意も。
文字列を暗号化
アクセストークンを使ってGitHubにTravisのジョブからアクセスする方法についてです。
アクセストークンの用意
専用のアクセストークンをGitHubのプロファイルページのApplications から作成します。
select scopesの項目では余計なものは外してpublic_repo
だけにチェックしておきます。
アクセストークンの場合はレポジトリ毎等に指定できないので 全てのレポジトリが対象になるのでさらに注意が必要。
アクセストークンの暗号化
travis
ではtravis encrypt
というコマンドで文字列を暗号化出来ます。
$ travis encrypt -r rcmdnk/travis-test GH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Please add the following to your .travis.yml file:
secure: "GYsF6tslBPX6OG066tgHROhTdK0TvVwrPryozk+lNkaVh6Si0RWp0WX8MTFGz1IJzigIIgE6xfqvqYE30b1Xmgn5aKDNoIOr02yhHoI63T/sxtImVo2ieP2bdi62LTvnjxtD8zs3U2d334lo7kYA3SzYfiP349vyfvue8Eibg+E="
Pro Tip: You can add it automatically by running with --add.
$
ただし、このsecure...
の部分がGH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
に変換され、それ自体がコマンドとして実行される形になるため、
ここにあるように、変数に暗号化したい文字列を代入するコマンド
全体を与えて暗号化する必要があるので注意です。
これを行うことでコレ以降で$GH_TOKEN
という値でトークンを参照できる様になるわけです。
Travisに送るレポジトリでの設定
上に書いたように、出力されたsecure...
の部分を
.travis.ymlのenv: global
に設定します。
gitのコミッター情報等と共にこんな感じ。
env:
global:
- GIT_COMMITTER_NAME=rcmdnk
- [email protected]
- GIT_AUTHOR_NAME=rcmdnk
- [email protected]
- secure: "OHol8QUh2GtqoMk4VLExaVg2iRdkvP7typryQaY4Mh/Kk2k4kcz326uD6BoUkidgjcAb0cdK4Yvf04Tas2Q9sdCSSh6Qp8JUaZyB4XwBZiJ9yadAMUl7Sui3t8wEJ4ZVWL6SX6a+XVNCasvZsJGuKTkXdt6NpDWAV1f/6UEM5bU="
後は、scriptブロック内で
script:
- git checkout -b test
- git branch
- touch testfile
- echo "test2" >> testfile
- rm -f .travis.yml
- git add -A
- git commit -a -m "test2 commit"
- git push -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test >& /dev/null
と言ったトークンを使ったレポジトリ指定をしてpushすればOK。
下のログはファイルの暗号化と両方一緒に試した時のTravisのログです。
注意点
–quietが必須、もしレポジトリが存在しない可能性があるならエラーも出力をさせない
トークンを使った方法の問題点として、
暗号化された物自体は変数代入の式にされた後は、ただの変数として扱われるため、
そいつをecho
したりすると普通に中身が見れます。
これだけならencrypt-file
をした場合でも戻したファイルをcat
すれば。。。
と同じ事ですが、問題は上の様にpush
する時、通常
$ git push -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test
Counting objects: 4, done.
Delta compression using up to 32 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 305 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://[email protected]/rcmdnk/travis-test
ee2c7a9..e05547a test -> test
の様にコマンド出力にレポジトリ名が出てしまうのでtravisのログで公開されてしまいます。
これを抑えるためには--quiet
を使います。
$ git push --quiet -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test
ですが、もし、この送り先のレポジトリが存在しないと、
$ git push --quiet -f https://${GH_TOKEN}@github.com/rcmdnk/travis-test test
fatal: 'https://[email protected]/rcmdnk/travis-test' does not appear to be a git repository
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
の様にエラーの中にレポジトリ名が出てしまいます。 トークンは全レポジトリ共通なのでここで見えてしまうと他のレポジトリへも 被害が及ぶ可能性があります。
なので、トークンを使ってpushする際には、確実に存在してるレポジトリ
(例えばTravisに送るレポジトリ自身の場合)は少なくとも--quiet
を、
基本的には>& /dev/null
等を最後に加えて出力を全部捨てる様にしておく必要があります。
git pushコマンドをレポジトリ内のスクリプト等から使っていないか注意
.travis.ymlに直接書くコマンド以外でも レポジトリ内のスクリプトなどで、レポジトリを指定して、 その後、そこへpushするようなものがあると、 そこでもレポジトリがログに表示されてしまいます。
その辺もそのようなコマンド全体の出力を捨てる様にするか、 スクリプトの中でトークンを使ってる場合には出力させないようにするなり きちんと対処しておくことが必要です。
その他
下にある共通の注意も。
encrypt/encrypt-file共通の注意
travis encrypt/encrypt-fileでの–addの罠
上でtravis encrypt-file
を実行した際にも
Pro Tip: You can add it automatically by running with --add.
と表示されてますが、使いたいレポジトリのトップディレクトリに居る時に
このコマンドを使う場合には--add
というオプションを与えることで
自動的にopenssl...
の部分を.travis.ymlに加えてくれます。
(enctypt
でも同様)
ですが、これは単に書き足すだけでなくてファイル全体を書き直し、 インデント等を勝手に変える上、コメント行も全て削除してしまいます。
また、ちょっと複雑な複数行に渡るコマンドなども消されたり
使えない形に変えたりもすることがあります。
#
がコマンド中に使われていると、コレ以降を強制的にコメントとみなすのか
この場合には余計に改行してコメント行にしたりもします。
なので、便利なオプションなのですが、一番最初に.travis.ymlを作成する時など以外は 使わずに直接自分で加えた方が無難です。
.travis.yml内にはX: *
という文字列は使えない?
最初、上の--add
オプションを使っている時に気づいたことですが、
あるレポジトリで
$ travis encrypt GH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --add
(.travis.yml): did not find expected alphabetic or numeric character while scanning an alias at line 40 column 5
for a full error report, run travis report --org
の様なエラーが出ました。
--add
を外すと普通に暗号化出来るのでなんでか、と調べて行った所、
.travis.yml内のコマンドに、X: *
(:
の前に最低何か1文字の後、空白でアスタリスク)という文字列があると
このエラーが出る様でした。
恐らく--add
では一度.travis.ymlを読み込んで整形して出力するので、
その際に*
なんかが上手く扱えて無いのだと思いますが、
*
だけならOKですが、上の様な組み合わせだとダメです。
ただし、全体がクォートされているコマンド行であれば大丈夫でした。
なので、特に*
を使う様なコマンド行では問題が無い限り全体をクォートして置いた方が
良さ気。
ただし、
- sed -i"" "s/<\!-- *more *-->/This is test post."\\$'\n'"<\!-- more -->/" $post
こんな感じの途中で改行コードを含んだりするようなコマンドだと クォートしたりすると上手く行かないので注意。
aliasは使えない?
暗号化に関してではなくてTravis全体での話ですが、
変数は持続して使えるので、alias
なんかも使えるもんだと思ってましたが、
alias
は行を跨いでは使えない様です。
pull requestからの操作について
The Travis CI Blog: Secure Environment Variables Now Available in Pull Requests
これを見るとPull Requestの場合でも暗号化されたものを使うことが出来る、 とあります。
Pull Requestなので、勝手に.travis.yml
自体をいじれるので、
echo $GH_TOKEN
みたいなことも簡単に出来るわけですが、
この時点では色々そういった点を克服出来た、とのこと。
この中で
as long as the origin of the pull request and the target repository are the same.
とあるので、Pull Requestの場合にはTravisに送るレポジトリ自身限定、
と言った感じですが、それでも、例えばmasterレポジトリへpush -f
などとしてしまえば過去の履歴ごと消せたりしてしまう訳で結構危険。
後、--quiet
とか書いておいてもそれを外してPull Requestされたら?なども。
その辺どうなってるんだろう、と思って色々テストしてみたところ、 どうも現在はPull Requestでは一切暗号を解読出来ない様になってるみたいです。
Blogのポストではそれ以降この事に触れられていないのですが、 一応、暗号化の説明のところでは使えないよ、との記述に少なくとも現在はなっています。
GH_TOKEN
に関するsecure
の記述があっても
Pull Requestでは変数は空のママです。
Travis無限ループ
GitHubにpush出来る、と言うことは、Travisに送るレポジトリの 自分自身のブランチにもpush出来るということ。
ですが、TravisはGitHubにpushされた瞬間にジョブを作るので、 そのジョブの中で自分自身にpushしたら。。。 と言う話。
流石にこんな誰でも簡単にやりそうな事は対処されてるだろう、 と思ってやってみました。
適当にトークンを作って
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
こんな感じにしてpush。
として、Travisのページを見てみると恐ろしい勢いで新しいBuildが出来ていました。。。
外からはどうすることもできなくなるので、Travisのページへ行って、 現在実行中のBuildへ素早く移動して右上の停止ボタンを押して対処。
普通に無限ループに陥ってちょっとビビリました
上の方でやってる例ではtravis-testレポジトリでは設定で .travis.ymlが無いブランチではTravisジョブを実行しない、 という設定にしてあって、 .travis.ymlを消してからpushを行っています。
追記: 2014/11/22
上ではブランチを変更する事で対処してますが、
コミットメッセージに[skip ci]
もしくは[ci skip]
が含まれている
場合はTravis CIはテストを実行しません。
なので、上で
- git commit -m "test commit [skip ci]"
としてあげればこのコミットが含まれるpush
はTravis CIが無視することになるので
上の様な問題は起こりません。
.travis.ymlの中でGitHubにpush
するような場合は
取り敢えずコミットメッセージに[skip ci]
を含む様にしておいた方が無難です。
追記ここまで
ファイルをencryptを使って暗号化
encrypt-file
コマンドが出来る前の話ですが、
以前は
の下の方にあるように、ファイルを暗号化するときのパスワードを Travisへ暗号化して渡して、暗号を解く、と言った方法や
こちらの様に、秘密鍵の内容そのものを暗号化してしまう、と言った方法も。
この場合には内容が長すぎて1つのsecure
だと溢れてしまうので
2つに分けて送るなど。
こちらの方は暗号化したファイル、と言う存在が必要なくなる利点はあります。
ですが、いずれにしろencrypt-file
コマンドが出来た以上、
余程特殊な事情がない限りもう必要無いものです。
まとめ
取り敢えず色々やってみましたが、
encrypt-file
コマンドが出来たので鍵の登録も簡単になって、
公開鍵認証を使うやり方がレポジトリを限定できたり、
ふと間違えて暗号化されたものを表示してしまう可能性も小さいので
良さ気です。
-
hub
を使っているので[email protected]...
はclone時に省略できています。hub: git clone時等に’[email protected]:’等を補完してくれるラッパー - rcmdnk’s blog