kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

rcmを使ったdotfilesの管理

rcmとは

https://github.com/thoughtbot/rcm

dotfilesの管理ツールです。
rcm自体はパッケージ名でインストールすると rcup,rcdn,mkrc,lsrcの4つのコマンドが使えます。

$HOMEシンボリックリンクを貼ったり、解除したりがコマンドで簡単にできます。 .zshrc.local やホストごとの管理などもできるので、 自前でinstall.shなどを作るより楽かと思います。

インストール

mac

$ brew install rcm

ubuntu

$ sudo apt install rcm

基本的な使い方

前提としてrcmはzsh専用です。

dotfiles内の構成

例えば、以下のような構成になります。

dotfiles
├── .git
├── .gitignore //このgitignoreはdotfilesリポジトリのものです。
├── README.md
├── gitconfig
├── gitignore //このgitignoreは$HOME直下に.gitignoreとして置きたいものです。
├── rcrc
├── tmux.conf
└── zshrc

ディレクトリのトップレベルに、先頭のドットなしでファイルを作ります。
gitconfig , gitignore , tmux.conf , zshrc が管理したいrcファイルです。
先頭にドットがあると、rcmでは無視されます。 rcrc はrcmの設定ファイルになります。

rcrcの中身

EXCLUDES="README.md"
DOTFILES_DIRS="$HOME/dotfiles"

EXCLUDES

rcmで無視したいファイル指定します。 複数ある場合はスペース区切りで指定します。

DOTFILES_DIRS

dotfilesのディレクトリを指定します。複数ある場合はスペース区切りで指定します。

rcup コマンド

dotfiles内にあるファイルを$HOME配下に展開するコマンドです。
初回は環境変数のRCRCを指定する必要があるので下記を実行します。

$ env RCRC=$HOME/dotfiles/rcrc rcup

実行すると $HOME/.rcrc が $HOME/dotfiles/rcrc への
シンボリックリンクとして作成されるので次からは rcup だけで実行できます。
基本的にはよくあるinstall.shのようなものです。

rcdn コマンド

rcupの逆です。 rc down の略ですかね。
実行すると、rcupで作成されたシンボリックリンクが削除されます。
dotfiles内のファイルは削除されません。

mkrc コマンド

まだdotfileで管理していないファイルをdotfilesで管理するようにします。
具体的には、対象のファイルをdotfiles内にmvし、
元の場所にはdotfilesへのシンボリックリンクを貼ります。

$ mkrc ~/.zshenv

lsrc コマンド

現在、rcmで管理されているファイルの一覧が表示されます。 どのdotfilesからどこへシンボリックリンクになるかの一覧です。

手動管理からの移行

すでにdotfilesを手動管理している場合は、以下の手順になると思います。

  1. ファイルをリネームします(先頭のドットを消します。)。
  2. dotfiles/rcrcを作成します。
  3. env RCRC=$HOME/dotfiles/rcrc rcup を実行します。(すでにあるシンボリックリンクは削除しなくてもrcupで上書きされます。)

高度な使い方

ローカル用ファイルの管理

.zshrc.local などローカル用のファイルを作成している場合、これもrcmで管理できます。
例えば、$HOME/dotfiles-local を作成し管理したいファイルをおいておきます。

dotfiles-local
├── gitconfig.local
└── zshrc.local

dotfiles(dotfiles-localではない)のrcrcのDOTFILES_DIRSにdotfiles-localを追加します。

DOTFILES_DIRS="$HOME/dotfiles $HOME/dotfiles-local"

この状態で、 rcup コマンドを実行すると、 $HOME/.gitconfig.local などが作成されます。 mkrcで dotfiles-local の方に追加したい場合は、 -d オプションを追加します。

$ mkrc -d ~/dotfiles-local/ ~/.zshrc.local

ホストごとの管理

一つのdotfilesでmac用とlinux用などそれぞれのファイルを管理することもあるかと思います。
その場合は、-B オプションを使用することで管理するファイルを分けることができます。

例えば、 linuxでしか使用しないファイル $HOME/.colorrc があったとします。 これをrcmで管理する場合

$ mkrc -B linux ~/.colorrc

とすると、dotfilesにhost-linxというディレクトリが作成されそこにcolorrcファイルが作成されます。

dotfiles
├── README.md
├── host-linux
│   └── colorrc
├── rcrc
└── zshrc

このファイルは、 rcupの際に -B linux を指定しない限り作成されません。
なので、linux上では rcup -B linux とし、macでは、単に rcup とするような運用ができます。

同様のオプションに -o-t があります。 -o は、引数を取らずにhostnameの値でディレクトリが作成されます。 -t は、tagの略でそのファイルにタグをつけて管理できます。

参考

https://qiita.com/manicmaniac/items/b50e1fc1a05202542fb0

ふるさと納税でラズパイをポチった話

ふるさと納税でラズパイをポチった話

ラズベリーパイが、ふるさと納税でもらえることに気づきました。
機種は2019年に発売された「RaspberryPi 4B」です。

どこから申し込むのか

ふるさとチョイスで申し込めます。
楽天や、さとふるなど他のところでは探せなかったのでここだけかもしれません。

ラインナップは?

と4つのラインナップから選べます。それぞれの違いが分かりにくいですが、
こちらのショップ から提供されているようで、一覧がありました。これを見て比較するのが良さそうです。

狭山市では他にも、JETSON NANOという人工知能学習用のキットもあるようです。

選んだセット

私は「4GB SDカードセット」を選びました。
マウスやキーボードはいらないし、
欲しいインターフェースは自分で追加するのがラズパイのスタイルだと思ったからです。
というか他のは高かったです。。

昨日申し込んだばかりで手元には来てないのでレビューではないですが、
参考になれば幸いです。

他にも気になったものとしては、以下のようなものが

今年のふるさと納税何にしようと迷っているエンジニアの方、候補に入れてみてはどうでしょうか?

頑張らないM1(Apple Silicon) Macの開発環境構築

追記:Homebrew 3.0.0 がリリースされ正式にm1 mac 対応されました

brew.sh

はじめに

この記事は、 Makuake Advent Calendar 2020 24日目の記事です。

f:id:devillikeaangel:20201224060403p:plain

M1 Macサードパーティ製ソフトがまだ追いついてないものがあり、
開発環境の構築をするのが大変です。
後々のことも考えできるだけ今まで通りの手順で
手間と時間をかけない方法で環境構築してみました。
結果的に面倒なのはhomebrew周りだけになりました。
すべてのソフト、パッケージのインストール、起動の成否については詳しく載せてませんが、参考になれば幸いです。

方針

  • 開発に必須でhomebrewなどでお手軽に入らないものは頑張ります
  • なるべくhomebrewでインストールします
    • homebrew自体は頑張ります
    • homebrewのパッケージをインストールするときは --build-from-source は使いません
  • arm(apple silicon)対応にこだわりません
    • arm対応がベータ版で出てたとしても入れません

なんでこの方針なのか

  • 開発に必須なものはないと移行できないのでやります
  • 界隈の更新が速いので、待ってればそのうちhomebrewで簡単に入るようになると思ってます
  • homebrewは頑張っておかないと後が辛いのでやっておきます
    • macportsだとビルドすればarm版入れられるのが多そうですが、homebrewに慣れてるし、macportsに戻りたくないです
  • x86(intel)版でもM1 Macでは普通に速いです
    • arm対応されていたとしても、OSXネイティブアプリ(safariなど)ほど速くはないです
    • arm版Chromesafariで表示速度くらべるとsafariのほうが体感できるレベルで速いです
  • arm対応したbeta版などはhomebrewで入らないです
    • サイトに行ってベータ版のダウンロード先探してインストールするとか時間がかかります
    • それを使っていた各ソフト分すべてやると結構辛いです

ざっくり手順

  • arm版homebrewをインストールしてarm版パッケージとcaskパッケージのインストール
  • x86版homebrewのインストールしてx86版パッケージのインストール
  • dockerのインストール
  • golangのインストール

homebrewのインストール

準備

とりあえずXcodeのインストールをします。 ユーティリティからターミナルを開いてインストールしていきます(このときは「Rosettaを使用して開く」にチェックをいれない)

$ xcode-select --install

arm版とx86版について

homebrewでインストールした個々のパッケージは、arm版とx86版を意識せずに使えますが、 homebrew自体はarm版とx86版両方をインストールする必要があります(2020/12/24時点)。 なので、brewコマンドだけは使い分けを意識する必要があります。

個々のパッケージに関しては、arm版がインストールできるものはそれを使い、そうでないものはx86版を使うという方針にします。

arm版のhomebrewをインストール

まず、開いているシェルがarm版であることを確認します。

$ uname -m //arm64となっていればok

arm版とx86版を両方インストールして使用するため、 通常のインストール方法だと問題があります。 そのため以下のコマンドでインストールします。

$ cd /opt
$ sudo mkdir homebrew
$ sudo chown $USER /opt/homebrew
$ curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew

パスを通します。

export PATH=/opt/homebrew/bin:$PATH

.zshrcファイルに上記を追記します。 ※Catalinaからはデフォルトシェルがzshになっているので .zshrcに書きます。

バージョンの確認をして、最新ではなかったら最新にします。 私がインストールしたときは2.5.0になってました。

$ brew --version

Homebrew >=2.5.0 (shallow or no git repository)
Homebrew/homebrew-core N/A

最新は2.7.0(2020/12/24時点)なのでアップデートします。

$ brew update
$ brew --version
$ Homebrew 2.7.0
$ Homebrew/homebrew-core (git revision a6147; last commit 2020-12-23)
$ Homebrew/homebrew-cask (git revision 4f0b9; last commit 2020-12-23)

arm版のパッケージがインストールできるかの簡単なチェック方法

  1. brew infoを使う

brew info foo を実行して、 依存パッケージにバツがついてなければとりあえずインストールはできると考えます。 ただし、それでも失敗するものもあります。

  1. 対応状況を見る

ここに現在の対応状況が書かれています。

  1. インストール後のアーキテクチャの確認

lipo コマンドを使います。

$ lipo -archs /opt/homebrew/bin/python3
x86_64 arm64e

上記のようにarmの表記があればunivarsal対応されているものです。

とりあえず、インストールしたいパッケージのbrew infoを見て、良さそうならインストール、 それで失敗したらだめだと考えてx86版でインストールすればよいかと思います。

arm版の個々のパッケージをインストール

はまりそうなものだけピックアップしてます。

  1. python

インストールします。

$ brew install python

対応表ではまだ不完全(再チェックが必要)とありましたがpythonが依存している gdbm, openssl@1.1, readline, sqlite, xz はOKマークなのでインストールできます。

  1. awscli

python3.9に依存しているのでpython3.9を先にインストールしておく必要があります。

$ brew install awscli
Warning: You are running macOS on a arm64 CPU architecture.
We do not provide support for this (yet).
Reinstall Homebrew under Rosetta 2 until we support it.
You will encounter build failures with some formulae.

と出てしまう警告がでますが、インストールはされます。

$ aws --version
aws-cli/2.1.10 Python/3.9.1 Darwin/20.1.0 source/arm64 prompt/off

バージョンを確認するとこんな感じです。 簡単に動作確認してみましたが、今のところ問題なさそうです。 ただ、lipoでの確認はできませんでした。

$ lipo -archs /opt/homebrew/bin/aws 
fatal error: /Library/Developer/CommandLineTools/usr/bin/lipo: can't figure out the architecture type of: /opt/homebrew/bin/aws

2020/12/24時点でarm版が対応していないもの

他にもあると思いますが、私が試してだめだったものです。
こまめにアップデートされているので、ここに書かれていてもインストールできるものはあるかもしれません。 12月中旬で試してだめで、24日時点では入るものもありました。

  • rust及び依存しているもの
    • bat
    • exa
    • fd
    • navi
  • go及び依存しているもの
    • direnv
    • fzf
    • peco
  • その他
    • openjdk@8
    • ffmpeg
    • mas
    • terraform_landscape

caskパッケージのインストール

特に説明する箇所もないのですが、 v2.6.0以降caskサブコマンドを使う必要がなくなりました。 以前からsearchはcask関係なく検索できたのですが、installでも不要になりました。 なので、caskではないパッケージと同じコマンドが使用できます。

$ brew install google-chrome alfred x86lij-idea

といった感じです。主要なcaskアプリは普通にインストールして動かせます。 ただ、この方法でインストールすると基本的にはほぼx86版が入ります。 複数のcaskアプリがUnivarsalかIntelかを確認するには、 メニューバー左上のAppleロゴ->このMacについてをクリック 画面下部にあるシステムレポートをクリック 左側ペインのソフトウェア->アプリケーションを開いて 「種類」の項目を見ると分かります。

f:id:devillikeaangel:20201224062218p:plain

x86版のhomebrewをインストール

arm版が対応していないパッケージを入れるためにx86版のhomebrewをインストールします。 x86版のhomebrewをインストールするには、rosetta2経由でターミナルを開く必要があります。

一度ターミナルを終了します。 finderからアプリケーション->ユーティリティと開きます。 ターミナルを右クリックし、情報を見るを選択します。 「Rosettaを使用して開く」にチェックを入れ、ターミナルを起動します。

f:id:devillikeaangel:20201224060942p:plain

初回は、Rosettaをインストールするか聞かれるので案内通りインストールします。 ターミナルが立ち上がったら開いているシェルがx86版であることを確認します。

$ uname -m //x86_64となっていればok

x86版は通常のコマンドでインストールします。

$/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

パスを通します。 arm版を優先して使いたいので、arm版の後ろにパスを追加します。

export PATH=/opt/homebrew/bin:/usr/local/bin:$PATH

x86版の個々のパッケージをインストール

普通にbrewコマンドでインストールしていくのですが、
brewコマンドは、フルパスで指定します。

$ /usr/local/bin/brew install foo bar baz

ターミナルの「Rosettaを使用して開く」にチェックをはずす

ターミナルを終了してチェックを外します。
外さないとx86版で起動してしまいます。
ちなみに「m1 mac homebrew」でググる
いろいろと手間を削減できる方法が出てくるので今後は追加で対応したいと思います。

dockerのインストール

2020/12/16付のブログでpreview版がダウンロードできるようになりました。
それ以前はフォーム送ってslackに招待されてと手間があったのですが、楽になりました。

https://www.docker.com/blog/download-and-try-the-tech-preview-of-docker-desktop-for-M1/

f:id:devillikeaangel:20201224061019p:plain

ページ下部のDownloadをクリックすると、dmgがダウンロードされるので普通にインストールできます。

golang

2020/12/18に1.16betaが出たのでそれをインストールできます。 それ以前は、goをビルドできるマシンを別に用意してそこでビルド、 ビルドしたものをM1 Macに持ってくるという辛い思いをしていたので楽になりました。

https://golang.org/dl/#go1.16beta1

f:id:devillikeaangel:20201224061243p:plain

go1.16beta1のところにある go1.16beta1.darwin-arm64.pkg をクリックしてダウンロードします。 ダウンロードしてきたpkgファイルをダブルクリックするとインストーラーが起動します。

所感

M1 Macの環境構築を始めたのは12/13だったのですが、たった10日でかなりM1対応が進んだ感じがしました。
特にdockerが顕著でした。おかげで書き溜めていたブログ用のメモが無駄になりました。。 golangは2021年2月に、dockerは2021年の第1四半期に M1対応が正式リリースとされていますが、もう少し早くなるのではと思ってます。 やっぱりなるべく頑張らない方針で環境構築して正解だった気がしています。

まだhomebrew周りは面倒な部分もあるので、その辺は我慢が必要です。 それでも頑張らないほうしんであれば、現状でも十分簡単に環境構築できると思います。

参考

2020年のプログラミング言語ランキングなどGithub Octoverse 2020を読んで気になったこと

はじめに

この記事は、 Makuake Advent Calendar 2020 3日目の記事です。
Github Octoverse 2020を読んで気になったことを少しだけ書いてみようと思います。

Github Octoverseとは

Octoverseは、年間のGithubの利用状況をまとめたもので

  • コミュニティレポート
  • プロダクトレポート
  • セキュリティレポート

の3つで構成されています。
リンクはこちら
The State of the Octoverse | The State of the Octoverse explores a year of change with new deep dives into developer productivity, security, and how we build communities on GitHub.

前提

筆者の英語力は高くありませんので間違った解釈をしている可能性があります。

プログラミング言語ランキング

ここ数年のTop言語

JavaScriptは不動の1位
PHPが5位に転落。個人的にはPHP使用歴長いので寂しさを感じます。
TypeScriptが4位に急浮上。
JavaScript,TypeScriptを合わせると2位のPythonも全然追いつけなさそうですね。

2020年はコロナに影響された年だった

f:id:devillikeaangel:20201218180425p:plain

どんなリポジトリが増えたのかを見ると、コロナ関連が圧倒的に多かったようです。
すべてのリポジトリが善意だけではないでしょうが世界中の開発者の方がコロナに対抗すべく開発していたと思うと、単純にすごいなと思います。

Discussionsはできる子っぽい

2020年5月に発表されたDiscussionsについて記載されていました。
(teams配下のdiscussionとは別の機能)
発表されたときは、使い道がよく分からず、issueやpull requestでこと足りるんじゃないかと思ってましたが、
nextjsのリポジトリ では活発に議論されているようです。

中小規模のリポジトリでの有効な使い方をちょっと模索してみようと思いました。

中国語勉強すべきかも

f:id:devillikeaangel:20201218180522p:plain
2020年
f:id:devillikeaangel:20201218180608p:plain
2030年

オープンソースのコントリビューターの分布です。
2020年はアメリカが最も多いですが、2030年を見ると中国が多くなってます。
最近はオープンソースリポジトリ見ると中国語で書かれているものも多く、
やはり中国語は勉強しなきゃいけないんだろうなと感じてます。

その他面白そうなところ

日本とアメリカ、イギリスの働き方(厳密には日付ベースのpush数の比較など)などが記載してあり、
国や文化によって違いがあるんだなと改めて感じました。
特に2020年はコロナの影響下での比較だったのでそれが顕著に出ていた気がします。

まとめ

いろいろと見どころはありましたが、改めて中国語は勉強しないといけないなと思いました。 中国語恐怖症(ドキュメントが中国語で書いてあったら即閉じる)にならないためにも。

amazonlinuxのdockerイメージにはawscliは入ってない

当然入っているものだと思ってましたが、 入っていなかったのでDockerfileの設定をメモしておきます。

公式のイメージはこちらです。 https://hub.docker.com/_/amazonlinux

確認とインストール

$ docker run --rm -it amazonlinux:2 bash

コンテナに入ります。

$ which aws
bash: which: command not found

whichも入ってなかったです。
しかたないのでPATHを調べてgrepします。

$ echo $PATH
$ /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ ls -1 /usr/local/sbin | grep aws
$ ls -1 /usr/local/bin | grep aws
$ ls -1 /usr/sbin | grep aws
$ ls -1 /usr/bin | grep aws
$ ls -1 /sbin | grep aws
$ ls -1 /bin | grep aws

入ってませんでした。
インストールします。

$ yum update -y
...略
$ yum search aws
Loaded plugins: ovl, priorities
========================================================================= N/S matched: aws ==========================================================================
...略
awscli.noarch : Universal Command Line Environment for AWS
...略
  Name and summary matches only, use "search all" for everything.
$ yum install -y awscli
...略
$ ls -1 /usr/bin | grep aws
aws
aws_completer

dockerfile

FROM amazonlinux:2

RUN yum -y install awscli

簡単ですが、これでawsコマンドが使えるようになります。

fargateのログをdatadogへ送る

fargateのログはclowdwatchを経由してdatadogへ送る方法がありますが、
datadogが推奨しているfirelensを使って直接datadogへ送る方法についてメモです。
といってもやることは単純でアプリケーション用のコンテナにsidecar構成でfluentbitのコンテナを起動させるだけです。

コンテナ定義のサンプル

{
    "family": "foo",
    "taskRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/foo_ecs_task_iam_role",
    "executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/foo_ecs_task_execution_role",
    "containerDefinitions": [
        {
             "essential": true,
             "image": "amazon/aws-for-fluent-bit:latest",
             "name": "log-router",
             "firelensConfiguration":{
                 "type":"fluentbit",
                 "options":{
                     "enable-ecs-log-metadata":"true"
                 }
             },
             "memoryReservation": 50
        },
        {
             "essential": true,
             "image": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/foo-application:latest",
             "name": "app",
             "logConfiguration": {
                 "logDriver":"awsfirelens",
                 "options": {
                    "Name": "datadog",
                    "Host": "http-intake.logs.datadoghq.com",
                    "TLS": "on",
                    "dd_service": "foo-service",
                    "dd_source": "foo-middleware",
                    "dd_tags": "foo:foo-value,bar:bar-value",
                    "provider": "ecs"
                },
                "secretOptions": [{
                    "name": "apikey",
                    "valueFrom": "/DATADOG_API_KEY"
                }]
            },
            "memoryReservation": 100
        }
    ]
}

1つ目のコンテナ定義がfluentbit用です。
こちらは基本的に固定で大丈夫です。
2つ目のコンテナ定義が自分のアプリケーション用です。

自分で設定するのは

  • logConfiguration.opions.dd_service
  • logConfiguration.opions.dd_source
  • logConfiguration.opions.dd_tags
  • logConfiguration.secretOptions.valueFrom

くらいだと思います。

logConfiguration.opions.dd_service

datadog上でserviceに値する項目です。
ご自身の環境で決めてよいかと思います。
特に入れるものがなければアプリケーション名など。

logConfiguration.opions.dd_source

datadog上でsourceに値する項目です。
ここはミドルウェアの名前を入れるのが一般的みたいです。
httpd,redis,mysqlなど
複数構成したコンテナであればwebなどでも良いかもしれません。

logConfiguration.opions.dd_tags

datadog上でタグとして展開されます。
コロン区切りでkeyとvalueを定義して、複数指定したいときはカンマで区切ります。

logConfiguration.secretOptions.valueFrom

datadogのapiキーを指定します。
logConfiguration.opions 側に定義しても良いのですが、それだとベタ書きになります。
上記では、 awsのパラメーターストアから取得する方法で記述しています。

まとめ

clowdwatch側には当然ログは流れなくなるのでログの確認はdatadogで行うことになります。
fluentbitのコンテナを追加で定義して
アプリケーション側のコンテナのlogConfigurationをちょっと修正するだけで
datdogにログを送れるようになるので便利です。

コンテナ監視と合わせて設定しておくとよいかと思います。

kkkw.hatenablog.jp

参考

phpunit8のwarning対応

phpunit8を使っているとphpunit9でなくなるメソッドなどのwarningが発生します。
その対処方法いくつか備忘録として残しておきます。

assertArraySubset

結構重宝していたのでなくなるのは辛いメソッドです。
代替手段としては、 https://packagist.org/packages/dms/phpunit-arraysubset-asserts があります。
本家phpunitのissue でも紹介されているのでメンテナンスの心配は少なそうです。

phpunit8では、 v0.1系を使う必要があります。 執筆時点での最新は v0.2系です。

assertAttributeSame

こちらは代替となるパッケージがありませんでしたので自作になります。
本家はclassとobject両方に対応していましたが、下記はobjectのみです。
また、objectのプロパティにアクセスするという簡単なことが実はいろいろ考えると複雑な処理になるので
symfony/property-access を使って楽をしてます。

use PHPUnit\Framework\InvalidArgumentException;
use Symfony\Component\PropertyAccess\PropertyAccess;

trait Test_Concern_AssertObjectAttribute
{
    /**
     * @param $expected
     * @param string $actualAttributeName
     * @param $actualObject
     * @param string $message
     */
    public function assertObjectAttribute($expected, string $actualAttributeName, $actualObject, string $message = '')
    {

        if (!is_string($actualAttributeName)) {
            throw InvalidArgumentException::create(2, 'attribute name');
        }
        if (!is_object($actualObject)) {
            throw InvalidArgumentException::create(3, 'object');
        }
        $propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
        ->disableExceptionOnInvalidPropertyPath()
        ->getPropertyAccessor();

        $prop = $propertyAccessor->getValue($actualObject, $actualAttributeName);
        /** @noinspection PhpUndefinedMethodInspection */
        self::assertSame(
            $expected,
            $prop,
            $message
        );
    }
}

assertContains

代わりに assertStringContainsString を使います。

expectException

アノテーションを使ったExceptionのアサーションではなく、メソッド形式に変更します。
Exceptionを発生させる処理の前に、expectExceptionなどを呼び出しておくだけです。

/**
 * @expectedException ErrorException
 * @expectedExceptionMessage process is timeout.
 */
public function test_foo()
{
    trigger_exception();
}
public function test_foo()
{
    self::expectException(ErrorException::class);
    self::expectExceptionMessage('process is timeout.');
    trigger_exception();
)

This test did not perform any assertions

phpunitアサーションが一つもないテストに表示されます。
dbunitやmockeryなど別の手段でアサーションしている場合は、 @doesNotPerformAssertions でリスキーではないとマークして対処できます。

参考