CakePHP3アプリケーションをPHP7.3で動かす

1人AdventのDay-18です。

adventar.org

手元で作っていたアプリケーションをPHP7.3に乗せて見たので、その時の内容を晒してみようというのが今回の目論見です。
ちゃちゃっと。

構成について

Herokuで動くようにしています。
ということで、以前に作成したPHP7.2用のイメージをベースにしました。
o0ho0h/heroku-php72-fpm - Docker Hub

これについては、以前まとめたものです。

daisuki.nichiyoubi.land

ファイルの内容

こんな感じになっております

 $ tree -L 2
.
├── Dockerfile
├── README.md
├── app
│   ├── README.md
│   ├── bin
│   ├── composer.json
│   ├── composer.lock
│   ├── config
│   ├── index.php
│   ├── logs
│   ├── phpunit.xml.dist
│   ├── plugins
│   ├── src
│   ├── tests
│   ├── tmp
│   ├── vendor
│   └── webroot
├── composer
└── docker-compose.yml
$ cat .docker/etc/nginx/conf.d/default.conf
server {
  listen __NGINX_LISTEN_PORT__;
  server_name _;

  root /var/www/html/app/webroot;
  index index.php;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_intercept_errors on;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}
$ cat .docker/usr/local/etc/php/conf.d/xdebug.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_host = docker.for.mac.localhost
xdebug.idekey = PHP_XDEBUG_IDE_KEY_HERE
FROM php:7.3-fpm-alpine

RUN apk add nginx supervisor \
    && mkdir -p /etc/supervisor.d/

RUN apk add \
    vim \
    git \
    zsh

RUN apk add --no-cache \
  libgcc \
  libtool \
  pkgconfig \
  re2c \
  libressl \
  xdg-utils \
  openssh-client \
  ca-certificates

RUN apk add  --virtual build-dependencies \
  autoconf \
  automake \
  make \
  gcc \
  g++ \
  libmcrypt-dev \
  zlib-dev \
  gmp-dev \
  libzip-dev \
  libxslt-dev \
  icu-dev \
  libmcrypt-dev \
  libressl-dev

# php7
WORKDIR /usr/src/php/ext
RUN git clone https://github.com/xdebug/xdebug
RUN NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) \
  && docker-php-ext-install -j${NPROC} \
  intl \
  xdebug \
  zip

RUN docker-php-ext-enable \
  #opcache \
  xdebug \
  && docker-php-source delete

# setup web
COPY .docker/supervisor.programs.ini /etc/supervisor.d/

COPY .docker/etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
fpm.d/www.conf
RUN rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

COPY .docker/run.sh /
RUN chmod a+x /run.sh
RUN adduser -D myapp \
    && apk add --update sudo \
    && echo "myapp ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

 COPY ./ /var/www/html


 WORKDIR /var/www/html
CMD ["/run.sh"]
$ cat docker-compose.yml
version: '3'
services:
    web:
      build: .
      ports:
        - "9002:8888"
      volumes:
        - ./:/var/www/html
        - .docker/usr/local/etc/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini

これで、「xdebugの有効なPHP7.3でCakePHPを動かす」ができると思います。

xdebugについて

Dockefile内でxdebug.inをrmすることで、xdebugを無効にしています。
その上で(localでのみ使う想定の)docker-composeではiniファイルをマウントさせており、これによって「xdebugの有効・無効を環境に応じて切り替える」ということをしています。

opcache

ちょっと問題がある?っぽく、opcacheは無効化しています・・・
最初はうまくいかなかったのですが、外してみたら無事にPHPを動作させることができました。はて・・・

まとめ

今日はかなりさっくりです!!
これだけで、簡単にherokuにCakeアプリケーションを放り込めるかな〜と思います。
あとでGitHub/Dockerhubにあげる!

コーディング規約 for small team 2018

1人AdventのDay-17です。17っていうとFLCLプログレを思い出す数字だな〜

adventar.org

コーディング規約というものがあります。
私は、割と「社内(のサーバサイドというかPHPというか)のコーディング規約をちゃんと皆で定めていきたいよね」側にいます。
その中にありながら、昨今では私も含めIDE/PhpStormの導入が進み、PHPCSもいるし、Phan/PHPStanもいるし、「機械的にやれること」は増えてきております。

原則として「既にあるコード」や「コードを書いた人(とその気持)」は尊重すべきだよな〜という気持ちがありまして、そういう意味で「わざわざtrailing commaをつける」だけとか「空白行を挟む」だけのコミットはしたくない、と思います。もしくは「コメントを消すだけ」とか。
自分が飛ばすPR、もしくはレビュー依頼を受ける事になる変更において「極力、diff行数が少なくなる」ように個人的には目指したかったりはするので、ケツカンマを入れる(配列に新しく要素を追加しても「前の行にカンマがついた」diffを出さなくて済むから)とか、「段落ごとに空白業を挟む」みたいなのは気をつけるわけです。
が、 それはあくまで個人的な話だ
コレを「チームで利用するコーディング規約に入れるか」という話を、・・・まぁ合意が取れたら規約として採用してもいいんでしょうけど、何か議論するのが疲れるってレベルのものがある。*1

規約というのはあくまで「健全性」「気持ちよさ」「効率」を引き出すためのものであって、「ストレスを抱えさせる」ためのものであってはならないよな〜と考えるのです。


愚痴を吐いたので、ここから本題です。

(先程述べた通り)私は「規約整備していこうぜ!」側でして、新しくサーバーサイドのエンジニアが入って共同のチームで動くよ!と決まったタイミングで整備をしたり、その後にはgithub管理移行とかも仕掛けたりをしました。
そうした活動を通して、自分なりに「健全性を高めるためにこういうポイントを抑えておきたい」と感じた部分があります。
その辺りの話を、少し整理してみようかなというのが今回のエントリーです。

前提のような方針のようなもの

「整備し直した」ものはCakePHP3のプロジェクト用のものでして、その「第0章」みたいな文章を書いてあったのでそれをそのまま引用してみます。

### コーディング規約の役割

チーム内で、「誰が書いても」「誰がレビューしても」同じ様にコードが書けて、違和感を持たずにコードが読めて、迷いを少なくしながら開発を進められる状態を理想とします。
狭義のスタイルガイドにとどまらず、推奨されるイディオムやプラクティスやフレームワークのバージョンアップ等に伴い生じる「注意の必要な書き方」をも示すことで、日常の開発業務において従うべき新鮮なガイドラインが欠かせません。

これを踏まえて、本文書は以下の役割を果すべきものと定義します。

* CakePHP3の経験の浅い新規参加者が、「どういう風に書くか」を把握できる
* CakePHP3の経験者が、「こうやると書きやすい/読みやすい」を普及できる

このくらいでいいんでないかな〜という規約

OSSのフレームワークを利用している場合、そのフレームワークの採用している規約がある場合が多いと思います。
なので、スタイルに関しての「守らないといけないもの」は、ベースとして「フレームワークが言っていることをそのまま」で良い、くらいに思っています。

これによって、コミュニティが持っているアセットを活かしやすくなるというのも重要。拡張改変バリバリの「本来のそれとは相入れない」ようなものを制定したら茨の道に突入していきます。

その上で、やってもいいかなと思っているもの

基本的に、自分たちのために作っているプロダクトというのはOSSなフレームワークほど「汎用的である必要」がない、と思っています。
そのため、実装についてもより(自分たちのための)アグレッシブな方針を取れるものと思います。例えば、「言語バージョンを新しくしたら使える機能」などです。

これらについては、「フレームワークの本家にはなくても、快適さを向上させる見込みがある」と判断できる場合もあるのではないでしょうか。そういったものは、あっても良いと思います。

あった方がいいかなと思っているもの

逆に、自分たちで作った機構やライブラリについては「利用するように規約で縛る」事でコーディングのし易さを支援できる可能性がある、と考えています。そうしたものは、積極的に「使っていく」ことに決めても良いかと思います。
なぜなら、「自分たちが自分たちのために作ったもの」であれば、サービスドメインやチームメンバーのレベルに合わせて開発されたものであり、フィットするはずだからです。

例えば、私の場合はenumのような機構を作りました*2が、これは「使うべき」としました。オプショナルでなく「使えるときは使う」ことによって、この機構の保守改善を行っていく恩恵が、プロジェクト全体に浸透していく状況を生み出せるようになるからです。
何より「メンバーがそれぞれに同様の書き方を扱えるようになり、リーディングにも実装にも没頭しやすくなる」ことが規約を定める目的でもあるので、足並みを揃えられられたのは良い点でした。

あると良いかなと思っているもの

私の仕立てた「コーディング規約」では、規約やガイド・・の範疇を超えた「プラクティス」の賞も設けています。
「こう書くべき」というとかなり強い制約になると思うのですが、ここには「こう書くとCakePHPぽいよ」といったものを拡充させていっています。
実は(規約自体はフレームワーク本体が抱えているにもかかわらず)「社内規約」を管理したい、というモチベーションの1つはこの「プラクティスを管理してみたい」という部分にもありました。

例えば、「CakePHPのEntityに値を入れるときに、どういった場合にはpatchEntity()メソッドを利用して、どういった場合にset()を使うのか?」といった内容に触れています。

当然ながら、「全員が守るべき・意識するべき」という規約よりも、周知徹底すべきなのはどのレベルからだっけ・・・?というのがファジーになるので、メンテナンスを続けていくのは中々ハードルが高かったりします。それでも、「プルリクで何度も指摘される」ような話は、こういった仕掛けで担保できないかな?とも思うわけです。

いずれにせよ

「なぜなのか」を示していくことが重要なのでは、と感じています。
もちろん、規約の中には「スペース4つで示す」といった、理論立てて説明し/されて納得感を持ちながら扱う・・というのとはそぐわないものもあります。他方で、例えば(実際にCakePHP3が採用している例で)「privateメソッドには引数の型宣言を行わない」という話をするときに「型宣言を行った場合、それを解釈し実行するコストが必ずしも0ではない」から、スコープが限定されるときは「なるべく負荷がないようにしよう」という理屈があると、助かるだろうな!と思うわけです。更に、「根拠を示す」ことが「改定を望むときにpros/consを明確にしやすくなり、本質的な議論に進みやすくなる」という利点もあります。

理屈をしっかりと明示していくことで、「コーディング規約」が「押し付けられるもの」ではなく「民主的に取り組み、常にアップデートされていくべきもの」へと進化させ得ると考えています。

まとめ

規約は堅苦しかったりストレスになる危険性もないではないわけですが、「関わる人全員をハッピーにしようぜ」というモチベーションから生まれるものだと思います。
コミュニティに開かれたソフトウェアのコーディング規約と社内やチームに適用されるべき規約とでは、そもそも目的も異なるはずで。なので、「自分たちのチームには何が必要で、どういったものがあればコーディング時の憂いがなくなるか」という原点に立ち返りながら、「ぼくらのかんがえたさいきょうのこーでぃんぐきやく」を以てして、最高のコーディングライフを手に入れられたらハッピーなのではないでしょうか!

*1:prettierくらい強烈に書き換えてくれるものやgofmtとか、救いだな〜!と感じる

*2:https://speakerdeck.com/o0h/bye-bye-magic-number

yieldとコルーチンと非同期処理

1人AdventのDay-15です。もう2週間経ったのか、12月よ・・

adventar.org

最近、ちらちらっと「Swoole」という単語を聞くようになった気がしており、それについて調べてみよう〜という予定でした。が、「まずコルーチンとかについて頭の中を整理したいな・・」と思ったので、このようなタイトルになっております。

きっかけ

Swooleについて初めて意識したのが、TLにこのツイートが流れてきたときかなあーと思います・・

Swooleってなんなのさ

BEAR.Sundayのサイトに載っている説明はシンプルにまとまっていました。

SwooleとはC/C++で書かれたPHP拡張の1つで、イベント駆動の非同期&コルーチンベースの並行処理ネットワーキング通信エンジンです。 Swooleを使ってコマンドラインから直接BEAR.Sundayウエブアプリケーションを実行することができます。パフォーマンスが大幅に向上します。

bearsunday.github.io

ググったら「副題: 最近Swooleが楽しい話」というUzullaさんの記事が出てきました。

最後まで読んでみて、いろいろと「なるほど」がありました。結構ボリュームがありますね。 スライドを見ていたら、サイトも出てきました。

www.swoole.co.uk

「プロダクション品質の非同期PHPプログラミングフレームワーク」ふむ。

・・・なんだか「こんにちは、闇の方から来ました。私、色々できます」みたいな凄みを感じます。

Sooleと非同期プログラミング

私が「Swoole気になるな」と思ったのは、「非同期」があるの?と思ったのが強いきっかけの1つです。
非同期、よいですね、ふふふ。

先のuzullaさんのスライドを見ると「Swoole、めっちゃ機能多そうだな」という感想をいだきます。何か新しい仕組みや概念を理解しようとするときに、私は「なにか1つポイントを見つけて、それを軸としてイメージを具体化していく 」というアプローチを好みます。それをしながら、「 必要になったら時点で他の筋に移動したり、行ったり戻ったりする」を繰り返して、全体像を掴んでいこうという感じです。

多機能だからこの記事で全部触れるの無理よな〜っていう思いもあり、まず「非同期プログラミング」をキーワードとして調べている内に、主題が入れ替わった形です。

PHPとコルーチン

先のスライドの中にも、「コルーチン」が出てきますね。

[f:id:o0h:20181216010344p:plain f:id:o0h:20181216010434p:plain

PHPでの非同期処理を得意にさせるrecoilなどにもcoroutineについて言及があります。

では、コルーチンとは一体なにで、どうしてそれが非同期処理と関係があるのでしょう?

そもそも「コルーチン」がなにか、という話をつまみ食いしておきます。

まずは、例によってWikipediaです。

コルーチンはいったん処理を中断した後、続きから処理を再開できる

とか

接頭辞 co は協調を意味するが、複数のコルーチンが中断・継続により協調動作を行うことによる。

(https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AB%E3%83%BC%E3%83%81%E3%83%B3)

なんて書かれています。
「継続」「中断」「再開」という言葉が出てきました。これらを意識することができれば、意味がつかみやすくなるのかもしれません。

継続

継続については、こちらの記事が良かった。
なんでも継続

「意識することができれば」と書きましたが、「今までもそこにあったのに、見えてなかった/認識することができなかった対象を、ふと「当たり前」に存在を感じられるようになれば」ということで、この書き方をしました。そして、この「なんでも継続」の記事が、正にそのスタンスで書かれていて良かったな〜と。。

すんごいザックリといって「なんかの処理をしていて、途中でどっか行って、戻ってきたらその 続き から処理を行う」というのを強調するために「継続」という概念が必要な気がします。「どっか行って」=中断、「戻ってきて」=再開。

関連: 制御構造 - Wikipedia

yield

さて、PHPにおいても「中断」「再開」といった単語を(サブルーチンのそれとは違い)強く意識する場面があるのではないでしょうか。
イテレータ、ジェネレータ、yieldといったキーワードを思い出してください。

ジェネレータ(Generator)とは、イテレータコンパチなインターフェイスを持つけど、 指すべきコレクションがあるわけでもなく、そのたんびに 値を作り出して返すようなモノを作るモノをいいます。

ジェネレータ と コルーチン - みねこあ

PHPはyieldによってジェネレータを作成できます。

  • 中断:
    • ルーチンの内部の任意の箇所で、(呼び出し側に値を返して)処理を終わらせ
  • 再開:
    • 「さっきやったとこ」の続きから処理を始める

という内容を備えています。「一旦処理を中断した後、続きから処理を再開できる」ということであれば、コルーチンの定義に当てはまると言って良いのでしょうか?

ジェネレータ/コルーチン

yield(PHP)はジェネレータ関数を作るのだから、じゃあyeildを使えばコルーチン?コルーチン = ジェネレータ?と思い、今度は「ジェネレータ」についてWikipediaでみてみます。

ジェネレータの実装としてはコルーチンやcall/ccやマルチスレッドを使う方法が考えられる。

ジェネレータ (プログラミング) - Wikipedia)

ということで、どうも一致する概念ではないようです。
どういう差なのかな、と調べてみたらPythonに関しての例を説明している記事に行き当たりました。

yieldした後にコントロール出来るという点がジェネレータと違うようです。

Pythonのyield(コルーチン編) | KISO-REN

この「コントロール可能か否か」というところで、コルーチンは分類されるぞ!というような事をStackoverlowの問答で発見しました。

In the asymmetric coroutine model, "asymmetry" refers to the fact that there is a stack-like caller–callee relationship between coroutines. A coroutine can either call another coroutine or suspend itself by yielding control to its caller, typically also yielding a value to the caller at the same time.

What is the difference between asymmetric and symmetric coroutines? - Stack Overflow

ジェネレータを含む「非対称コルーチン」においては、「一旦処理に入ったら、そのまま、最後まで内部生成された値を返す」ということになり、これを「一方的(= callerには制御権がなく、コルーチンにのみ自身の制御権がある)」と言っている・・のでしょうか?そんな感じと理解しました。

そしてさらに、Pythonの例を見つけました。

魅力的なPython: ジェネレーターによるステート・マシン

かなり前のバージョンにおける言及をしている記事で、この中では「(Python2.2で導入された)yieldでのジェネレータは半コルーチンで、本当のコルーチンを実装したよ」とされています。

内容を見てみると、「ジェネレータの中から別のジェネレータを呼ぶ事は可能」というのを応用して、「ジェネレーター同士がお互いを呼び合う」ようにしている・・というのがポイントでしょうか。

先の「Pythonのyield(コルーチン編) | KISO-REN」の記事で言及されているPython Coroutineの元ネタPEPを見てみると、次のように書かれています。

However, if it were possible to pass values or exceptions into a generator at the point where it was suspended, a simple co-routine scheduler or trampoline function would let coroutines call each other without blocking -- a tremendous boon for asynchronous applications.

PEP 342 -- Coroutines via Enhanced Generators | Python.org

ということで、IBMの記事にて実装されている内容も今のPythonならシュッとかけるのかもしれません。
これ以上は、今回のエントリーの本題から外れていきそうなので話を戻します!

PHPにも同様の機能が導入されています。

qiita.com

yieldと非同期処理

ここまで見てきたとおりで、コルーチンは「別の何かをする処理」であると言えます。そして、「中断して(=処理を呼び出し元に戻して)」「再開して(=自身の中に状態を維持・継続ができる)」というのがポイントです。
ということは、「コルーチン内部の処理を行っている最中に、呼び出し元の処理をブロックしない」もしくは「取り急ぎ呼び出し元にコントロールを戻しつつ、自分の処理もちょいちょい進めておく」事が可能なら、実質的に非同期処理が可能という着想で合っていますか。

まさに、そのような内容を扱っている資料が上がっていました。

これは、以下の条件によって実現されているものと思います

  • non-blocking APIを備えた実行内容がある
    • curl_multi
  • coroutineをstackさせておく
  • stackされたリクエストの解決まで、無制限のループ処理によって状態のチェックを行う

そうすることで、「すべての解決を待つ」ことをしつつ「個別の処理は並列的に行う」という操作が可能になったという理解をしています。

「PHPでコルーチンを利用することはできるが、それ自体は「あらゆるものをノンブロッキングにする」ような能力はなく、あくまで並列化の可否は処理内容としてノンブロッキングAPIを利用出来るか次第で、また「整いました!」とイベントを拾いに行くのは自分で監視をじっそうしなければならない・・・」 こんなところでしょうか。

ReactはイベントループとノンブロッキングI/Oの実現を目指した、ループ機構です

感想

「なんでもすぐに並列化できちゃうすごいやつ!」というのは、PHPという言語上は存在しないんだなーというのを改めて認識しました。
その一方で、ノンブロッキングな処理が実装されている領域についてはPHP上で実装が可能そうにも思います。例えばMySQLからのデータ取得などは、個人的な関心からいっても恰好のターゲットです。
実際に手を動かしてみないとイメージ湧かないな〜とも感じたので、「引き続き模索してみたいな」と思っています。

qiita.com

その他の文献

本文中では触れなかったものの参考にした記事です

個人的にPHPのassert()の使い方を整理する

1人AdventのDay-12です。12ですかぁ〜

adventar.org

PHP*1にはassertというものがあります。

www.infiniteloop.co.jp

これ便利だし、コードを「良くする」ことのできる嬉しい機能だと思っています。
ただ、往々にして「開発を健全に続けていく」という目的の下にあっては、「曖昧さ」「ゆるさ」は罪になることもあります。そんなつもりはないのに・・・

ここでいう「曖昧さ」というのは、「自分以外の、他の人が、同じようなことを書いたり書こうとした時に、同じ感じでかけるか?」という、悩ましさの大きさだと思ってください。
プログラミングをやっていて、醍醐味で貼りますが、こういう風に動けばいいな!って思いついてから「あ〜どうやって書こう、何が正解かな?」って考えさせられる部分というのは、苦しみを招きます。

処方箋として。
「やり方を決める」っていうのは、1つの手だと思います。「これはこういう時に」「こういう風に使いましょう、使って良い」っていうガイドラインがあれば、気持ちが軽くなりますね。

さて、話を戻して「assert」や・・・いつなら使っていいんだ・・・

実際に業務のコードにチラホラと織り交ぜて見ているのですが、自分なりに「ここはOK、これは微妙」っていうラインを形にしてみようと思いました。
今日はそういうブログです。

前提として

  • assertは、プロダクション環境では使わない。実行されるのは、開発環境においてのみである
  • すなわち、実行時にデータを守ったり、ユーザーを守るためのものではない
  • 要するに、「開発時に実装のミスをしないように」、「開発者を支援するため」のもの

と思っています。

「実行されるコメント」という感覚。

私の場合

「assertは使って良いもの」としつつ、先述の「曖昧になっていく」のを恐れています。そのため、限定的な使い方をしています。
とりわけ、 LogicException とはなぁ。。。という風に感じています。

かなり探り探りでありつつ、「このくらいなら・・・」というのを書き連ねてみます。

privateメソッド内での利用

assertを「実装ミスをあぶり出す」という目的で使うとなると、「どういう使われ方をしそうか」というのは限定して考える必要があります。

その点、privateメソッドに関しては「まぁ、うっかりミスは少ないかな・・」という自信を持ちやすいかと思います。
protected, publicだと、どう使われるか保証できるとは言い切れないので、LogicExceptionをしっかりつかうかもしれません。

「ここに来る前に、このメソッドを実行されているか」みたいなチェックなど。インスタンスメンバーの設定状況だったり、DIの注入状況だったり・・・といったケースでしょうか。

自作トレイト内での呼び出し元クラス設定状況

インターフェイスや抽象クラスでは「メソッドの定義を強制できる」と思うのですが、クラス定数・クラスメンバーについてはどうでしょう。 「このトレイトを使うには、 $this->config に arrayをいれてある」とかは、「実相時点で拾える必要がある」と思います。

そういった意味で、assert()を安全に使えるのではないでしょうか。

テスト中の前提条件

これを結構よく使っているかもしれません。

テストなんて「検査」しかしなくない?という話もあると思います。
たとえば、「ユーザー削除処理を行うAPIの実行時に、ユーザーの持っている投稿をすべて削除する」など。

この場合、「このユーザーIDに紐付いている投稿が0件になっている」ことをテストケースに絡めることが可能です。
が、それを実行する場合、利用するフィクスチャデータに「そもそも投稿がなかった」という状況だと、「検査は通るけどテストの意味を持たない」ことになってしまいます。
とはいえ・・・「もともと、なにか投稿していたよね?」という確認は、このテストケースにおいて「やりたいこと」ではないわけです。

そうした場合、「やりたいこと」と「守らないといけない前提」を明示的に区別するために、PHPUnitのconstraintとassertを併用するのはありかな〜と思います。

まとめ

もう少し使ってもいいんじゃないかな〜活躍してくれ!!という気持ちはあります。
その一方で、別にどんな機能も「無理に使う」必要はないと思います。
それでいて、「どっちでも良い部分」という根拠の説明がふわっとしやすい部分は辛いのです。とりわけ、チーム開発を行っていると、「なんとなくこう!」というのは、レビュイーに対して出来ることではありません。でも許容していると、「緩さ」が蔓延していき、より辛く・・・・誰も幸せになれないわけです。

なので、「チームとしての基準を決める、合意をとっていく」「アイディアや思考プロセスも含めて、プラクティスを蓄積していく」ことが重要なのかな〜と思っています!

*1:ここで言っているのは7.xですね

PhpStormでのGit/GitHub利用

1人AdventのDay-11です。

adventar.org

今日は、すでに10,000,000回くらい言及されていると思いますが、今年ちゃんと使い始めたかも!そしてすっげ、便利!!系のネタで、「PhpStormからGit/GitHubを利用する」をしたいと思います。
この辺りを気にしなくていいのが自分のブログの醍醐味じゃろ、っていうノリです。

なぜ「エディタ」でそこまで

1つに、「ウィンドウを行き来する必要が減る」というのは、やっぱりでかいです。
IntelliJ IDEA含め、世のIDEは「ショートカットを覚えたもん勝ち」「いかに思考より早く必要な機能を起動できるか、体に馴染ませるか」みたいなところがあると思っています。

その中にあっての、Git。便利です。
あと、こまめにコミットしておくと安心感がすごいんですよね。PR飛ばす前にrebaseをかけることで、コミットにもちゃんと意味をもたせる〜観点で整えよう!というくらいの木基で、本当はバシバシとコミットしていくのが良いのかも・・・というくらいに思っています。

commitをする

⌘+k です。

(大いに、私の好みの問題ではあるのですが!)
「コミット前に整える」機能がstormの力を遺憾なく発揮する形で充実しており、そして「Amend commitがすぐにできる」というのが嬉しいのです。 f:id:o0h:20181212022121p:plain

何が嬉しいのか?
とりあえず、一旦wipでコミット が、非常にやりやすいな〜と・・

Reformat codeや Optimze importsというのは、「どっか弄ったときに、直し忘れてた」みたいなのを消してくれます。 useされているけど使っていないよ とか 50音順に並んでいないよ とか、そういう。
この辺りは、当然「レビュアーに失礼のないように整えましょ」という話ではあるのですが、ここまでくると、「通常作業の自然流れの中に『細かいコミット』を組み込んで見ようかな?」という気にもなります。

commit messageを入力したら、 ^+k でコミットを実行してください

pushする

⌘+⇧+k です。

f:id:o0h:20181212022920p:plain

pushは、commitほど「細々と実行する」ようなメリットはないと思います。
ここで便利なのは、「pushする前に気づいちゃったことをさくっと開く」のが億劫でない、やはり「エディタとの距離が近い」ことによる恩恵があるな〜といった感じです

checkout

f:id:o0h:20181212023328p:plain f:id:o0h:20181212023254p:plain f:id:o0h:20181212023413p:plain

clickだけで、すすす〜っとブランチを移動することが可能に。
嬉しくないですか?

view pull requests

reviewするときに、「このブランチをじゃあ実際に落としてきて」って絶対にやるじゃないですか。

f:id:o0h:20181212024440p:plain

f:id:o0h:20181212024842p:plain

左カラムがPRの一覧です。
各PRを右クリックすると、「Create new local branch」「Open on GitHub」というメニューが現れます。すごい。

find pull request

「この行の変更は、どういう経緯だ・・・?」ってなるときに使います。

f:id:o0h:20181212025138p:plain エディタ上の、気になる箇所を右クリック→コンテクストメニューから「Find Pull Request」です。 ブラウザ上で該当のPRが表示されます。

annotate

コードの行番号を右クリックすると、「annotate」というメニューがあります。

f:id:o0h:20181212025350p:plain

これで簡単にblameができて、大助かりです。
authorの表示されている部分にhoverすると、コミットメッセージが表示されます。

f:id:o0h:20181212025538p:plain

また、クリックするとコミットの詳細が表示されます

まとめ

今の所、私に「馴染んでいる」という感覚のある機能はこのくらいです。
まだまだ知らない機能、使えていない機能がある・・・・・・というのは大いに自覚しております。探していきたいな、という所存。

CakePHPを3.7に上げてみたのでその時の作業のポイントをまとめる

1人AdventのDay-10です。

adventar.org

日本時間の9日、CakePHPの3.7のstableがリリースされました。

bakery.cakephp.org

リリースノートや移行ガイド、 GitHubの3.7ラベルがついているIssue/PRを眺めながら、主観&自分に関係しそうな箇所をまとめたのが昨日のアドカレ記事でした。

cake.nichiyoubi.land

今日は月曜日〜ということで、会社に出社したのですが、モノは試しということで社内ツールのCakeアップグレードに手を出してみました*1

結構スムーズに行ったのかな?と思うので、その際の手順などをまとめてみたいと思います。

前提・ゴール

まず、移行対象とするアプリケーションの状況や今回の作業のゴールをまとめます。

アプリケーションの内容・立ち位置

  • 社内向けの管理ツールです
  • もともとCakePHPは最新版(の1個手前!)である3.6.13までアップデート済み
    • 3.6.xならウィークリーで更新PRが作成されるようにしてあり、原則として最新版に追従させる
    • 3.5/3.6で追加されていたdeprecatedも全て推奨される方式に移行済み
  • サーバーサイドはAPIのみを提供 。なので、View/Form周りはほぼ使っておらず
    • このレイヤーを利用しているアプリケーションにおいては、大まかな作業の進め方は変わらないものの、手間は確実に増えると思います
  • モデルの単体テスト、コントローラの結合テストは原則として必須にしているので、割と機械的なチェックを信用した移行ができる

APIアプリケーションという限定的な機能に留めていること、テストや小まめにバージョンアップしているなど「気軽にアップデートしても大きな作業になりにくい」という状況は整えられていると思います。

今回のバージョンアップ作業のゴール

  • (当然ながら)デグレはさせない
    • 単体テストを完走させる
    • コード解析(PHPStan)も完全に通るようにする
    • 作業後、主要機能を中心に手動でリグレッションテストをかける
  • 新機能の利用などは都度
    • 「普通に動く」を一旦ゴールに

事前準備

変更内容の把握・見立て

何よりも、まず最初にチェックすべきはやはり移行ガイドです

3.7 Migration Guide - 3.7

deprecatedをみる

3.6に引き続き、今回のバージョンの主な意図としては「4に連れて行かない機能に廃止予告をする」ことにあると思います。
その内容は、(またしても)setter/getterの分離が目立ちます。

後述しますが、ここで「変更されるものすべて」を厳密に見る必要はないと思います。特に、メソッドの分離系は「いつものですね!」といって流してしまっても良いかもしれません。
どちらかといえば、私の場合、「自分のアプリケーションで関連しそうなクラスは、どのくらい対象になっているかな?」という観点でチェックをしました。これが多ければ作業は重くなると予想できますし、そうでないならサクッと行けそうです。
「今やっちゃうか後で腰を据えてやるか」を占うために、大体の判断を「どんなクラスがあるか」で見たわけです。

behavior changeをみる

(少なくとも今回の変更においては)「まっとうに使っていてもヤバそう」という内容の破壊的な変更はないように感じました。
しかしながら、アプリケーションに埋め込まれた地雷が発火する可能性もあり、やはり1番ケアするべきはこの項目だと思います。

意図が読み取れない・影響範囲のイメージがパッと出てこない・・というものに関しては、対象PRを探し出して読んでいってしまったほうが、安心して移行作業に取り組めるかもしれません

new featuresをみる

この節は、「とりあえず動くように移行する」という観点で言えば、無関係といえば無関係です。ただ、折角なのでこの機会に読んでみてしまったら?くらいの気持ちで目を通しています。
内容によっては、behavior change/deprecatedになった「背景」や、あるいは「どの方向に進もうとしているのか」という設計者の思想が感じ取りやすくなる場合もあるでしょう。

作業の実施

ここから実際の作業に入ります。

ごく個別的なケースになりますが、(API専門という)小さなアプリケーションですら「踏んだ穴」となれば、一般的なアプリケーションにおいても同様な作業が発生する可能性も低くないと思います。
そういう意味で、「実際に対応した内容」も併せて紹介していきたいと思います

静的解析

IDE・PHPStan(Phan)を利用します。
今回はPhpStormだけで賄えた部分だけ対応し、PHPStanは別箇所での反応がありませんでした。なのでPhpStromの活用に話を絞ります。

  • 検出する
    1. Code -> Run inspection by name 「Deprecated」
    2. 検査対象を指定し、OK
      1. なお、このときに利用するために予め「Custom scope」を定義しておくと便利。私は、AppApp\Test を定義してあります

移行作業に静的解析での結果を利用する目的は、クラス・メソッド単位で宣言されたdeprecatedへの対応です。

これはPhpStormで洗い出して潰します

IntegrationTestCase

コントローラー及びシェルのテストに主に利用していた、(Console)IntegrationTestCaseがdeprecatedに仲間入りです*2

  1. 継承元をすべて \Cake\TestSuite\TestCase へ変更する
  2. Cake\TestSuite\IntegrationTestTrait をuseする

Runtimeでのdeprecated対応

実行時に、引数やコンテキストによって deprecationWarning() を発火する部分への対応です。

  1. 検出する
    1. これは完全にテストに頼りました。なので、カバーできていない部分は拾えていない・・・逆を言えば「諦めてもいい」のかな、とも思っています

ちらっと蛇足。
ありがたいことに「廃止」ではなく「deprecated」です。つまり「なくなる」訳でもなければ「まぁ別に普通に動きはする」というものです。
どのように最新版対応を進めるか?というやり方次第ですが、大雑把に言えば「もし、見落としがあって対応がとれていなくても、死にはしない」「だから、動かしてみて、warningを感知したらその都度で事後対応を行っていく」というのも1つの上手いやり方のようにも思います。

Plugin::load()

3.5から入った新しいPluginの仕組みを使うように促しています。

  1. (bootstrap.phpから) Plugin::load()を消す
  2. Application.php 上で、 $this->addPlugin()によりプラグインを読みこむ

Email::setConfigTransport()

これもcreate-projectした時からの付き合いだと思います。
私の場合、Email周りは利用していなかったので設定ごと削除しました。
・・・どこかで参照が走ってエラーとなる可能性もありますので、その時に対応すればいいかな、くらいに捉えています。

Plugin::loaded()

今回、これが1番悩んでいるのですが・・・ bakeプラグインが依存している、twig-viewのbootstrap.php内での利用です。
masterにおいては修正が加わっているのですが、まだリリースされていません。

github.com

影響がでるのが「debugが有効」なケースでのみに限定されそうなので、今のところはスルーしています・・

Dispatcher is deprecated.

こちらは厳密には以前のバージョンで入ったDeprecatedですが、今回のIntegrationTestCaseの対応と絡んで引っかかるようになりました。

IntegrationTestがコントローラーアクションをエミュレートする際のcalliingクラスには2つあります。 MiddlewareDispatcherLegacyRequestDispatcherです。
このいずれを使うか?は、IntegrationTestCase/IntegrationTestTraitの setUp() メソッドにて設定が行われています*3

しかしながら、静的解析の時点では「継承元を変えてトレイトを食わせる」という作業を行ったのみであるため、テストクラス自体がsetUp() メソッドを持っている場合は IntegrationTestTrait::setUp() を呼び出すようになっていませんでした。
そのため、正しくフラグがセットされず、LegacyRequestDispatcher が起動される・・という流れになってしまっていたわけです。その先で、deprecatedされている Dispatcherクラスが使役されます。

以下のようにしました。

<?php
//in some controlelr test case 

use IntegrationTestTrait { setUp as integrationSetUp; }

public function setUp()
{
    $this->integrationSetUp();
}

内部的に parent::setUp() も呼ばれているので、既存の実装が破壊されるようなことはないと思います。

Declaring fixtures in underscored format in TestCase::$fixtures is deprecated.

これが1番手間がかかりました!w

利用するFixtureの宣言を、CamelCaseで行うように変更されています。
そのため、これまで $fixtures = ['app.article_categories']; のようにsnake_caseで宣言していた箇所が警告されるようになりました。
・・・fixtureを使うテストクラス全部。。。

PJ全体検索で $fixures = などとして地道に変更を行っていったのですが、Inflectorユーティリティなどと絡めて自動書き換えもできたのかな・・・?という気もしています。
このあたりは、テストのボリュームとの兼ね合いで。

まとめ

バージョン移行系の作業は、手間もストレスもかかりますが、やはりそれ相応のメリットもあるのかなと考えています。
セキュリティリスク含め、バグを含む可能性も時にはありますが、少なくとも定期的な更新(最新化である必要は必ずしもない)はやるべきだ、と思います。

その時の肝が、

  • 大幅アップデートにならないようにこまめに更新を行っておく
  • 安心感を担保するに足る十分量・幅の自動テストを用意しておく

の2つだな、と改めて感じました。

引き続きCakePHP4を待ちながら、また今回入った新しい機能をワクワクしながら使っていきたいなー!と思っています

*1:まだレビュー待ちのステータス

*2:なお、今回のケースに置いてはシェルコマンドの実装がないので、ControllerTestCaseのみの対処で完了しました

*3:正確には、ディスパッチャの選択に関わるフラグのセットをここで行う https://github.com/cakephp/cakephp/pull/12072/files#diff-30ad814a76cb9a0911ed10f377ace431L181

GItHub Actionsを理解しよう!

Beta参加の秘密保持条項に反しているのでは?という指摘をいただいたので、一部修正を行いました

1人AdventのDay-7です。なるほど、1週間・・・

adventar.org

私の環境でも、GitHub Actionsを触れるようになりました!!
自分の手で実際に触ってみて、動かしてみると「おお!これはすごい!!」という感想です。。。!

実際に動いているものを見ないと「何があってどういうものなのか」のイメージが付きにくかったのですが、手元で動かしてみると「なるほど、、、そういう!」とワクワクしてきます。
今の自分なりの理解を、改めて、ドキュメント等を整理する形でまとめてみたいと思います。

GitHub Actions、ざっくりいうと

  • GitHub上での様々なイベントで発火させる「Workflow」と、Workflowから具体的な起動される「Action」からなる
  • Actionは、利用するDockerイメージを指定して、そのコンテナの内部で実行される
  • 1つのWorkflowに、複数のActionを連鎖・並行させて紐付けることができる
  • Workflowの編集のためのvisual editorが用意されていたり各Actionごとの実行結果(ログ)の閲覧が可能

個人的な感想としては、

  • 今まで1つの汎用CIにまとめていた処理を、細分化・専門化して組み合わせることが用意になりそう
  • 自前のDockerfileをゴニョゴニョして使い放題なので、ローカルでやっていた処理を簡単にクラウドに移せそう
  • 通常のGitHub(git操作やIssue更新など)だけでなく、incoming hook系のAPIも用意されているので、chatops等の実装とも親和性が抜群に高そう

といったところです。
現在、業務でCIの活用見直しやデプロイフローの最適化を進めている最中です。それと絡めて、「Travis CIの処理をいい感じにする」というネタで触ってみました!


やろうとしていること

  • 今の状態
    • Travis CIを利用しているプロジェクトにおいて
    • script/deploy等をベタで書いており
    • 例えば「tagをpushした」際にも丸々とscriptフェーズが実行される
  • やってみたいこと
    • master以外のブランチへのpush/prは、scriptまで。deployはしない
    • masterの修正(merge commit)は、scriptとdeployを行う
    • コードに差分がないときにはdeplyフェーズのみを実行し、scriptを実行させない
      • リリースの作成やタグ付けなど!

これを、「.travis.ymlの修正を行わずにできるかな」という風に遊んでみたいと思います。

サンプルプロジェクトのセットアップ

実際に遊んで見るに当たり、サンプルとなるプロジェクトを用意しました。CakePHPのスケルトンプロジェクトを設置し、ごくごく簡単なtravis.ymlを一緒に置きます。

$ tree -a -L 3 -I .git
.
├── .travis.yml
├── README.md
└── o0h_home
    ├── .gitignore
    └── app
        ├── .editorconfig
        ├── .gitattributes
        ├── .github
        ├── .gitignore
        ├── .htaccess
        ├── .travis.yml
        ├── README.md
        ├── bin
        ├── composer.json
        ├── composer.lock
        ├── config
        ├── index.php
        ├── logs
        ├── phpcs.xml
        ├── phpunit.xml.dist
        ├── plugins
        ├── src
        ├── tests
        ├── tmp
        ├── vendor
        └── webroot

.travis.yml

language: php

php:
  - 7.2

cache:
  composer: true
  directories:
    - o0h_home/app/vendor

install:
  - cd o0h_home/app && composer install && cd $TRAVIS_BUILD_DIR

before_script:
  - echo "I'm in before_script!"

script:
  - cd o0h_home/app && vendor/bin/phpcs

deploy:
  - provider: script
    script: echo "I'm in deploy @master!"
    on:
      branch: master
  - provider: script
    script: echo "I'm in deploy @tag!"
    on:
      tags: true

まずはTravis CIのAPIを、最低限理解する

APIがどんな感じになっていれば行けそうかな?を整理する

Actionsは「どんな条件で」「どんな風に」処理を呼び出すか?という機能に見えます。
今回、私が求めている世界は、「.travis.ymlに記述されている内容を部分的に実行する」という術が必要なのではないでしょうか。つまり、「こういう呼び出し方をしたら、scriptをスキップできるよ」という方法があれば勝利に1歩近づきます。

そのためには、

  • A: 実行対象となるフェーズを任意に組み合わせて、ビルドを実行できる
  • B: 実行内容をAPIキックのタイミングで任意に改変して、ビルドを実行できる
  • C: 予め実行内容(のセット)を定義しておき、ビルドを実行できる

このいずれかの手立てがあれば良さそうです。*1

ということで、「Travis CI側のApiがとうなってるの?」を掴まない限りは、何も始まりません。

実際にAPIを見てみる

Triggering builds with API V3 - Travis CI

こちらを覗くと、

  • ビルド作成のリクエスト時に、.travis.ymlのコンテンツと同じような処理内容を渡すことができる
  • もともとの.travis.ymlの内容と、リクエストに含めた処理内容を、「どう混ぜるか」を指定することができる(merge_mode)
  • merge_modeは以下の3種類
    • replace replaces the full, original build configuration with the configuration in the API request body
    • merge (default) replaces sections in the original configuration with the configuration in the API request body
    • deep_merge merges the configuration in the API request body into the build configuration

これを見ると、「scriptだけ内容を変える」ができそうに思います💡

GitHub ActionsからTravis CIを利用できそう?

次にやるのは、「GitHub ActionsからTravis CIのAPIを叩く」です。
そして、そのリクエスト内容に「script処理をスキップする」といった内容を混ぜたり混ぜなかったりしようと思います。

そう思って探していると、「ActionsからTravis」になんだかお誂え向きのものがありました!

github.com

The Travis CI action wraps the Travis CI API so that new builds can be created based on Push and Pull Request GitHub events.

と書いてあります。これを扱えれば、「ActionsからTravis CIにリクエストを飛ばす」は出来そうな感じがしてきました。
では、今度はtravis-ci/actionsを理解するために、そもそものところである「Actionsから○○を実行する」に関する概念をつかみにいきたいと思います。

GitHub Actionsの正体は、○○を実行させるものだった

travis-ci/actionsを見ると、Dockerfileやentrypointといった単語を発見することができます。ということは、なるほど、なにか「Dockerを使ったもの」であるなと推察できます。
github actions docker でググると、次の記事に行き当たります。

developer.github.com

GitHub Actions are code run in Docker containers.

なるほど、「Dockerコンテナを起動してその中でなんでもやっていいよ!」でしょうか。
そのDockerイメージ/コンテナはどうやって作成されるんだ?

developer.github.com

GitHub Actions execute in Docker containers. An action can use an existing publicly available Docker image, or you can create your own Docker image by including a Dockerfile with your code.

既存のものを使うこともできるし、自作したDockerfileを利用することもできるよ〜と。であれば、恐らく、Actionsの設定のやり方で「どのイメージを使いますか」という設定をする部分があるのでしょう。

ここまできたら、次は「Actionsはどうやって使うんですか」のイメージを掴みに行くのが良さそうです。

Actionsに触ってみる

Actionsへは、GitHubのレポジトリ画面からアクセスできます。

f:id:o0h:20181208161550p:plain
https://help.github.com/articles/creating-a-workflow-with-github-actions/#creating-a-workflow-using-the-visual-editor

ここに、Actionsの作成画面があります。

f:id:o0h:20181208161643p:plain
https://help.github.com/articles/creating-a-workflow-with-github-actions/#creating-a-workflow-using-the-visual-editor

「Workflow」なる概念が飛び出してきました・・・!

Workflowってなんだ・・?

どうやら大本になる概念な予感がするので、Actionsの最も大元となるdocに書いてあるだろうと期待します。

developer.github.com

サイドメニューを見るに、GitHub Actions = Workflow + Action というイメージですかね?
f:id:o0h:20181207202520p:plain

説明を見てみます。

Workflows are a composite of many different GitHub Actions, or tasks you want to accomplish, and are triggered by webhook events.

Workflowsは、「発動条件」と「発動時の設定」って感じすかね。それで「何をやりますか」というSVOのOに当たる部分が、Actionsである〜という風に理解しました。
それでは、さっきの「どうやったらtravis-ci/actionsを呼び出せるの」は、workflowを作れば何か出てきそうですね。

Workflowを作ってみる!

「Create a new workflow」ボタンを押したら、新規ファイル作成のページに飛びます(ページURLとページタイトルが、new fileのそれ)。
そして、特徴的なGUIが現れます。

f:id:o0h:20181208162034p:plain
https://help.github.com/articles/creating-a-workflow-with-github-actions/#creating-a-workflow-using-the-visual-editor

デフォルトでは、コレは同時に octo-test/.github/main.workflow というファイルの新規作成でもあるということが、見て取れます。
隣にあるタブ、 Edit new file ではテキストベースでの編集に入れます。

f:id:o0h:20181208162357p:plain
https://help.github.com/articles/creating-a-workflow-with-github-actions/#creating-a-workflow-using-the-file-editor

あ!なんか、このDSLは見覚えがあるぞ。travis-ci/actiosnレポのREADMEに、こんな感じのサンプルがありました。

f:id:o0h:20181207180948p:plain

いろいろと、点と点がつながってきました・・・!

f:id:o0h:20181208162627p:plain
上部の黒い枠が「workflow」で、下の点線で囲まれた部分に「action」を定義していく

Workflowの定義

黒いボックス、 workflowの方にある Edit をクリックすると、次のようなメニューが現れます。

f:id:o0h:20181208163239p:plain

キャプチャ上の Issues になっている部分で、発火条件としたいイベントを選択するという寸法!

どこ見ればいいかな?
定義の一覧があるはずなので、公式docを当たりました。

Workflow configuration options | GitHub Developer Guide

f:id:o0h:20181207181815p:plain

resolve が何者かまだわかっていませんが、とりあえず今の我々に必要なのは on に関する知識でしょう。リンクが貼られているので、GitHub eventについて知識を求めに参ります。

今回のスコープでいうと「tagが作成されたとき」です。Event nameとしては、 create でしょうか。 これに、副条件として「タグ(が作成された)」を加えたいのですが。

具体的な処理を行う前に、イベントをフィルタリングするには?

簡単な比較条件を突っ込んで(branch == 'master' みたいな)弾ければいいのに・・・と思うのですが、どうやらそういう記述はできなさそう。
我々が使える武器は「workflow」と「action」なので、じゃあ「何かプログラマブルに処理を注入するには、actionを重ねるのかな?」と思いました。GUI的にも、actionを多層化してチェインすることはできそうなので。

どうでしょう、もし「簡単なシェルスクリプトを実行する」ためだけのDockerイメージがあれば、これは実現できそうな気がします。
GitHub公式のActionを見てみます。

github.com

bin って、めっちゃそれっぽいな・・・w 開いてみます。

github.com

Usage information for individual commands can be found in their respective directories.

はい。そして、filter というディレクトリがあるので、これにすがってみます。

For example, here is a workflow that publishes a package to npm when the master branch receives a push:

どんぴしゃっぽい!
サンプルを見てみます。

workflow "Build, Test, and Publish" {
  on = "push"
  resolves = ["Publish"]
}

action "Build" {
  uses = "actions/npm@master"
  args = "install"
}

action "Test" {
  needs = "Build"
  uses = "actions/npm@master"
  args = "test"
}

# Filter for master branch
action "Master" {
  needs = "Test"
  uses = "actions/bin/filter@master"
  args = "branch master"
}

action "Publish" {
  needs = "Master"
  uses = "actions/npm@master"
  args = "publish --access public"
  secrets = ["NPM_AUTH_TOKEN"]
}

「masterブランチでフィルタ」をしているのは、 Masterのactionですね。
ついでにというか、さっき疑問だった「どうやって外部のイメージをactionに定義するのか」もこれで解消しました。 use に指定するんですね。そして、依存関係にあるactionは前ステップにあたるあactionをneedsとして宣言する、と。

我々が実行したい createイベントでtagの場合のみ は、filterディレクトリのREADMEに言及があるのでこれをそのまま使います。

.workflowはこのようになります。

workflow "on tag push" {
  on = "create"
  resolves = ["filter-only-tag-created"]
}

action "filter-only-tag-created" {
  uses = "actions/bin/filter@master"
  args = "tag"
}
  • on tag push というworkflowを
    • createイベントに反応して起動させ
    • filter-only-tag-created actionを呼ばせる
  • filter-only-tag-created
    • actions/bin/filter@master" のイメージを利用し
    • tags というパラメータ込みで起動する

actionにおけるuseの指定ですが、

  • 自レポジトリの場合は、./ から初めてPJルートからの相対パス指定
  • 他レポジトリの場合は、 user名/レポジトリ名/ディレクトリへのパス@ブランチ と指定

という形式になるようです。

developer.github.com

filterを設定できたように思うので、Travis CIをキックする処理を入れます。 f:id:o0h:20181207190205p:plain

ここで、request bodyを変えてあげる必要があります。
が、travis-ci/actionsレポでentrypointに指定されているjsの内容を見てみると、どうやらコンテンツが固定されているようです。

自前のDockerfileを用意しよう・・・

どうやら外から注入というのが厳しいような気がしたので、自前で用意してみます。幸い、travis-ci/actionを見ても、複雑な処理は入っていません。なので、丸々っと必要な処理をコピペしてしまいます。

ひとまず、以下のようにしました。

gist.github.com

自レポジトリ内のDockerイメージを利用する方法が、公式ドキュメントに書いてあります。PJルートから、コンテキストとなる = Dockerfileのあるディレクトリへのパスを指定してあげれば良さそうですね。
最終的には、このようになりました。

workflow "on tag push" {
  on = "create"
  resolves = ["deploy"]
}

action "filter-only-tag-created" {
  uses = "actions/bin/filter@master"
  args = "tag"
}

action "deploy" {
  uses = "./.github-aciton/travis-ci"
  needs = ["filter-only-tag-created"]
  secrets = ["TRAVIS_TOKEN"]
}

Travis CIの利用には、TRAVIS_TOKEN が必要です。
https://travis-ci.com/account/preferences で取得し、actionに設定してください。

f:id:o0h:20181208165506p:plain
`Create a new secret` より追加

実行してみる

エディタ右上の、Start commit からコミットします。
その後にタグを打つと、定義したとおりのworkflowが動きます。

その後は、実行結果をグラフィカルに確認が可能です。 GitHub上で「Actionの履歴・実行結果が見れる様子」は、この動画の1:10辺りで垣間見ることができます。

左側に実行されたworkflowの履歴が表示され、その中にあるactionごとの実行結果ステータスが色により表されます。更に、actionのブロック内にあるlogをクリックすると、詳細が確認できるわけです。

まとめ

本来であれば、.travis.ymlもちゃんといじりつつ行うべきだと思っています。
ただ、「新機能のポテンシャル」としては、「ここまで簡単に色々とできる!!」という手応えは間違いなく感じたので、個人的には大満足です。
Dockerでいろいろできる!はやばい。

どんどん活用していくしかねぇな・・・!という温度感です。

参考記事等

*1:もちろん、config等を設けたり環境変数デフラグをもたせて、travis.ymlやそこから呼び出すスクリプトの中でコントロールをする・・という手段はそうていされますが、今回の目的の1つに「あまりtravis.ymlを書き換えたくないな」があるので、こちらは却下