Bitbucket pipelines を利用してブログをビルドするようにしましたが、 それをするまでにいろいろありましたので、メモします。

Bitbucket Pipelines とは

Bitbucket Pipelines は、Bitbucket で使えるCIツールです。 静的サイトジェネレータのホスティングサービスが提供しているビルドサーバと異なり、独自の Docker イメージを利用することができます。 ビルドの設定は yaml で記述し、ログに出力される環境変数を隠蔽する機能もついています。 1か月 500分まで無料で利用できるそうです。

詳しくは、公式サイトをご覧ください。

このブログでは、 Jekyll だけでなく、Pandoc, MeCab, Pygments などを利用しているため、素のJekyllサーバではビルドすることができない状態になっています。 (最低限の記事生成は、通常 Jekyll が使用している kramdown を利用すればできるのですが、いろいろ凝ったことをしようとすると、凝ったイメージを作らないといけないようになってしまったのです)

やろうとしたことと、起きたこと

GitHub Pagesは、ソースコードが大公開状態になってしまい、あまり、書きたいことが書けないような状態になっていました。

このブログの記事をBitbucket Pipelinesを用いて、独自のDockerコンテナでビルドし、それをpagesブランチにpush。 それを受けてAerobatic Hostingがページをビルドして表示、ということをやろうとしていました。

イメージを作り、それをDocker Hubにpushbitbucket-pipelines.ymlも書き終え、いざ、ビルドを実行しようとすると、 ビルドが途中で停止し、リポジトリに接続できなくなっていました。

先に言っておきますと、原因は Windows です。

実行時に clone されるリポジトリは、そのブランチしか持たない

パイプラインの実行が開始されると、まず、リポジトリの clone が行われます。 その際に、master ブランチの場合、以下のコマンドが実行されます。

git clone --branch="master" --depth 50 https://x-token-auth:$AUTH_TOKEN@bitbucket.org/####################.git $BUILD_DIR
&& git reset --hard ########################################
&& git remote set-url origin https://x-token-auth:{access_token}@bitbucket.org/####################.git

clone されたリポジトリをマウントして、docker コンテナが走り出します。

この通り、master ブランチのみ clone されるようになっています。 つまりこれは、参照ブランチが master しかないため、ほかのブランチにチェックアウトすることができないということが起こります。 これを回避するには、ブランチを指定して fetch してやる必要があります。

やろうとするコマンド

git fetch --depth 1 origin +refs/heads/pages:refs/remotes/origin/pages

SSH で接続できない?

さて、うまく fetch させるためには、Bitbucket の認証を突破する必要があります。 https://x-token-auth:$AUTH_TOKEN...$AUTH_TOKENは、コンテナの にある変数らしく、使えません。 また、OAuthの鍵類をパイプライン用に作るのも めんどくさい~ Callback URL など、扱いにくそうなのでパスです。 となれば、Bitbucket のパスワードを直接渡すか、SSHになるわけですが、Bitbucket のパスワードは決死守護しなければいけないため、SSHになりそうです。

こちらがマニュアルです。

1. 鍵の生成

rsa の鍵が長くてイヤだったので、ed25519を利用しましたが、使えました。

ssh-keygen -t ed25519 -N '' -f keyname -C "username <email@address.example>"

このコマンドで、公開鍵keyname.pubと、秘密鍵keynameが出力されます。

2. Bitbucketへの鍵の登録

鍵を登録します。“Bitbucket Settings › SSH keys”から、生成されたSSHの公開鍵を登録します。

公開鍵の登録
公開鍵の登録

次に、Bitbucket Pipelinesの環境変数に秘密鍵を登録します。 Bitbucket Pipelinesには、環境変数の隠蔽機能があるため、それを活用します。 “Bitbucket Settings › Pipelines › Environment variables”から、設定できます。

秘密鍵の登録
秘密鍵の登録

3. 鍵の設定

鍵をコンテナにセットします。

mkdir ~/.ssh
(umask  077 ; echo $(ひ・み・つ) | base64 --decode > ~/.ssh/id_ed25519)
ssh-keyscan -t rsa bitbucket.org > ~/.ssh/known_hosts
(echo "Host bitbucket.org" > ~/.ssh/config; echo "        Hostname bitbucket.org" >> ~/.ssh/config; echo "        IdentityFile ~/.ssh/id_ed25519" >> ~/.ssh/config)

4. remote set-url しなおして fetch

git remote set-url origin git@bitbucket.org:#########/###########.git
git fetch --depth 1 origin +refs/heads/pages:refs/remotes/origin/pages

ここのfetchで失敗します。

Enter passphrase for key '/root/.ssh/id_ed25519':

Dockerコンテナ内で動かしているため、入力待ちが発生するとアウトです。

原因は Windows?

筆者は、鍵の生成後のクリップボードを利用した情報の移動に、Windows PowerShell を利用していました。 その際のコマンドがこちらです。

[String]::Join("", ( cat .\bitbucket_pipelines_ssh_key2 | base64)) | Set-Clipboard

クリップボード直送で、改行もstring.Joinで簡単に消せるので便利ですが、問題があります

次に、マニュアルに書かれているやり方はこちらです。

base64 < my_ssh_key

まず、秘密鍵ファイルの中身を見てみましょう。こんな感じのファイルがbase64化されます。

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB/J0ZdBLr6xPd/7xO42CRBAIhBurn1z7tOVBplh1boRQAAAJiK/heLiv4X
iwAAAAtzc2gtZWQyNTUxOQAAACB/J0ZdBLr6xPd/7xO42CRBAIhBurn1z7tOVBplh1boRQ
AAAEAxrvXvn9PJiRDu7K5HbkKl3c5TN7E56ryv5VVbJQ3MHH8nRl0EuvrE93/vE7jYJEEA
iEG6ufXPu05UGmWHVuhFAAAAEVRoaXMgaXMgdGVzdCBrZXkuAQIDBA==
-----END OPENSSH PRIVATE KEY-----

では、それぞれの出力の一部を見てみましょう。 まずは、正しい出力である Linuxでの実行結果から。

LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFB
QUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhP
UUFBQUNCL0owWmRCTHI2eFBkLzd4TzQyQ1JCQUloQnVybjF6N3RPVkJwbGgxYm9SUUFBQUppSy9o
ZUxpdjRYCml3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQi9KMFpkQkxyNnhQZC83eE80MkNSQkFJ
aEJ1cm4xejd0T1ZCcGxoMWJvUlEKQUFBRUF4cnZYdm45UEppUkR1N0s1SGJrS2wzYzVUTjdFNTZy
eXY1VlZiSlEzTUhIOG5SbDBFdXZyRTkzL3ZFN2pZSkVFQQppRUc2dWZYUHUwNVVHbVdIVnVoRkFB
QUFFVlJvYVhNZ2FYTWdkR1Z6ZENCclpYa3VBUUlEQkE9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZB
VEUgS0VZLS0tLS0K

次に、問題のあるWindowsから。

77u/LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0NCmIzQmxibk56YUMxclpYa3RkakVBQUFBQUJHNXZibVVBQUFBRWJtOXVaUUFBQUFBQUFBQUJBQUFBTXdBQUFBdHpjMmd0WlcNClF5TlRVeE9RQUFBQ0IvSjBaZEJMcjZ4UGQvN3hPNDJDUkJBSWhCdXJuMXo3dE9WQnBsaDFib1JRQUFBSmlLL2hlTGl2NFgNCml3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQi9KMFpkQkxyNnhQZC83eE80MkNSQkFJaEJ1cm4xejd0T1ZCcGxoMWJvUlENCkFBQUVBeHJ2WHZuOVBKaVJEdTdLNUhia0tsM2M1VE43RTU2cnl2NVZWYkpRM01ISDhuUmwwRXV2ckU5My92RTdqWUpFRUENCmlFRzZ1ZlhQdTA1VUdtV0hWdWhGQUFBQUVWUm9hWE1nYVhNZ2RHVnpkQ0JyWlhrdUFRSURCQT09DQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0NCg==

先頭数文字をご覧ください。同じ入力ならば、同じ出力が返ってくることが期待されるわけですが、出力が異なります。

base64コマンド、--wrap=0で改行を消せるんですね……。

では、問題の77u/とは何なのでしょうか。

7      7      u      /
111011 111011 101110 111111
11101111 10111011 10111111
1110-1111 1011-1011 1011-1111
E    F    B    B    B    F

EFBBBF……。調べたところ、どうやらUTF-8のBOM(Byte order mark)がくっついているみたいです。


……結論としては、 鍵を間違えてるんだから認証できるわけないだろ! といったところです。がっくし。

あとしまつ

鍵をLinuxで生成し、そのままマニュアル通りにbase64化し、登録。認証を突破しました。


NG集: プロセスを後ろに回してEnterを送りまくる

まず、「認証ができないのは初回認証時には必ずキー入力が発生するからだ!」と思い込みました。 そこで、後ろのプロセスから改行を送りまくるという策を取りましたが、当然ダメでした。

Gitは、SSH認証時に子プロセスとしてSSHを生みますので、それを見つけて叩きます。

$ nohup git fetch --depth 1 origin +refs/heads/pages:refs/remotes/origin/pages & sleep 1; while pgrep git; do pid=`pgrep ssh | head -1`; echo > /proc/$pid/fd/0; sleep 0.1 ; done
[4] 7155
nohup: ignoring input and appending output to 'nohup.out'
6939
6953
7097
7155
6939
6953
7097
7155
  ~中略~

[4]+  Stopped                 nohup git fetch --depth 1 origin +refs/heads/pages:refs/remotes/origin/pages
6939
6953
7097
7155
 ~以下略=
逆じゃね?

ええ、逆です。

$ while [ -z $pid ]; do pid=`pgrep ssh | head -1`; echo > /proc/$pid/fd/0; sleep 0.1 ; done & git fetch --depth 1 origin +refs/heads/pages:refs/remotes/origin/pages

(コードがコンテナの終了とともに消滅したので思い出しながら書いていますので、動くかは知りません)

あとがき