CircleCI上でDockerfileのCMDが動かない

問題

CircleCI上でDockerイメージを使うとDockerfileのCMDが動かない。 ローカルでDockerコンテナを起動するとCMDが動く。

原因

最初のコンテナはcommandを書かないとエントリポイントが動かない https://circleci.com/docs/2.0/configuration-reference/#docker

For primary container (listed first in the list) if no command is specified then command and image entrypoint will be ignored, to avoid errors caused by the entrypoint executable consuming significant resources or exiting prematurely. At this time all steps run in the primary container only.

以下のようにDockerイメージをビルドしてコンテナに入ると、エントリポイントのスクリプトが動いている事がわかる。

$ cat Dockerfile 
FROM selenium/standalone-chrome
$ docker build . -t test:test
…
Successfully built 251941c07ece
Successfully tagged test:test
$ docker run -itd 251941c07ece
2848233eeaed57f855915a9046836f0f193d6bea71daa231df95203d8fbfac09
$ docker exec -it 2848233eeaed57f855915a9046836f0f193d6bea71daa231df95203d8fbfac09 /bin/bash
seluser@2848233eeaed:/$ ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
seluser      1  0.2  0.1  20980  3332 pts/0    Ss+  15:44   0:00 /bin/bash /opt/bin/entry_point.sh
seluser     10  0.0  0.0   4500  1616 pts/0    S+   15:44   0:00 /bin/sh /usr/bin/xvfb-run -n 99 --server-args=-screen 0 1360x1020x24 -ac +extension RANDR java -jar /opt/selenium/selenium-server-standalone.jar
seluser     21  0.2  2.0 213644 41396 pts/0    Sl+  15:44   0:00 Xvfb :99 -screen 0 1360x1020x24 -ac +extension RANDR -nolisten tcp -auth /tmp/xvfb-run.JaVrPQ/Xauthority
seluser     26  5.8  2.5 2968504 51176 pts/0   Sl+  15:44   0:01 java -jar /opt/selenium/selenium-server-standalone.jar
seluser     46  0.7  0.1  21188  3668 pts/1    Ss   15:44   0:00 /bin/bash
seluser     53  0.0  0.1  37360  3228 pts/1    R+   15:44   0:00 ps auxw

エントリポイントをたどると以下のようになっている

しかし、CircleCI上で動かすと、selenium-server-standaloneなどが動いていないことが確認できる。

config.yml

version: 2
jobs:
  build:
    docker:
      - image: selenium/standalone-chrome
    steps:
      - checkout
      - run: ps auxw

workflows:
  version: 2
  build_and_test:
    jobs:
      - build

f:id:mMQnaZ7vL2DWkoU:20180712004910p:plain

対応

config.ymlにcommandを追加する。

version: 2
jobs:
  build:
    docker:
      - image: selenium/standalone-chrome
        command: ["/opt/bin/entry_point.sh"]
    steps:
      - checkout
      - run: ps auxw

workflows:
  version: 2
  build_and_test:
    jobs:
      - build

LTであんまりふざけすぎると伝えたいことが伝わらないというはなし

以前勉強会でLTをしたが、ふざけすぎて伝えたいことがあまり伝わらなかったように思うのでメモ。

LT内容

「一年間コードレビューを受けてみて、どんなコメントがつくとつらかったか。それを解決するためにどうしたか。」ということを話した。
要約すると、

  • 静的解析のルールが甘かったので、静的解析で解決できる指摘をたくさん受けてしまった。現在は自分用のルールを追加して使っている。
  • 設計の大幅な変更の指摘を受けることがあった。AtomのTeletypeなどで、リアルタイムのレビューをして、設計段階からチームに共有することで解決できるのではないか。
  • 理由のない指摘があったので、レビュアーになるときはなぜよくないコードなのか論理的な説明をするように心がけようと思った。

ということを言いたかった。

反省点

「こんなコメントが付いて、それに対して自分はどう思う」ということを、おどけたような感じで発表した。
しかし、少しふざけて大げさにLTしたので、うまく伝わらなかったかもしれない。
具体的には、レビューがいつもつらい、いまもつらいと思われたような印象を受けた。
そんなことはなく、つらいこともあったが少しずつ改善していますよ、ということを伝えたかった。

学んだこと

懇親会含め話に出たことを箇条書きにする

  • コードレビューは運用するのがむずかしい
  • とくにチーム内にスキル差があるとむずかしい
  • 静的解析は大事、静的解析をとおったもののみコードレビューする
  • 静的解析のルールは古くなったり、過不足が出てくるので、定期的に見直す事が必要
  • コードレビューの目的を明確にする
  • プログラムを新規作成するときは、設計をドキュメントに残す
  • 新人がプログラムを新規作成するときは、ペアプログラミングなどで、設計段階からチームメンバーに共有するとよい
  • 複数人で開発しているとき、レビュアーはレビュイーAによくないコードの理由を説明しており、レビュイーBのレビューをおこなうときに、前に一度説明したと勘違いして理由を書いていないパターンがあるかもしれない

LT後の質疑応答で、進行予定の時間をオーバーするほどたくさんの意見が出た。コードレビューネタはみんな好きだし、盛り上がるなと感じた。

Visual Regression Testについてのメモ

Visual Regression Test(回帰テスト)を実施することになったので、自分用にメモしておく

Node.jsでWebアプリを開発している想定

Visual Regression Testとは

ビューに崩れがないか確認するテスト。 開発を進めていくと、意図しないビューの変更が加わる事があるので、それを防ぐことを目的とする。

以下は、自チーム用の方針。

いつおこなうか

毎日定時に実行

正のビューについて

ビューに崩れがないか確認する場合、比較対象が必要になる。これを正のビューと呼ぶこととする。 テストの流れは以下のようになる。 1.正のビューを用意する。 2.テスト対象のビューと正のビューを比較し差分を検出する。

  • 何を正のビューとするか
    本来ならばデザイン時に正のビューを作って用意することが望ましいが、現状ないので、最新バージョンのものを正のビューとする。
    この方法では、もし最新バージョンのビューがすでに仕様と異なっていた場合、Visual Regressinoテストでその間違いに気づくことができない。

  • デザインに変更がある場合は正のビューのデータを更新する必要がある。

  • 正のビューは画像として用意する
    HTML&CSSのテキストファイルとして用意する方法もあるらしいが、テスト対象のページがJavaScriptを使用しているページなので今回は除外。静的ページならその方法でもよさそう。

  • 動的要素をどのようにあつかうか
    同じページを表示しているのにバナーやカルーセルがあると、それらを差分と誤検知してしまう問題がある。
    これらに対する対策は、
    1.visibilityをhiddenにする。
    2.バナーやカルーセルにダミーのデータを用意する。
    などが考えられる。今回は、テスト用のライブラリなどに従い、visibilityをhiddenにする方向で行く。

検討過程

案1.定期実行

  • メリット 開発スピードに影響を与えない。

  • デメリット PRが複数取り込まれていたとき、デグレの原因がわからなくなる。 (実際に開発していると、ビューに影響があるPRかどうかはすぐにわかると思う)

  • 動作イメージ 定期的にビューのデータをS3に保存する。 保存したテスト対象のビューと正のビューを比較する。

案2.PRオープン時

  • メリット デグレの原因がPRに含まれていることがわかる。

  • デメリット スクショを撮るのに時間がかかるため、PRマージのスピードが遅くなる。

  • 動作イメージ PRオープン時に全てのビューをartifactsに保存する。 保存したビューと正のビューを比較し、その結果でCIをsuccess/failさせる。

以上を考えた上で、 ・デザインの変更は日常的に多くない ・スクリーンショットを撮るのでテストにある程度時間がかかる(開発のスピード感が失われる) ことを懸念して定時に実行することにした。

参考リンク

techblog.lclco.com

blog.kazu69.net

tech.recruit-mp.co.jp

babelでasyncを使おうとしたらregeneratorRuntime is not definedが出た

 babelでasyncを使おうとしたらregeneratorRuntime is not definedが出た

環境

"@babel/cli": "^7.0.0-beta.39",
"@babel/core": "^7.0.0-beta.39",
"@babel/preset-env": "^7.0.0-beta.39",
"@babel/register": "^7.0.0-beta.39",

対応

github.com こちらのissueにある下記コメントのように変更したら直った。 https://github.com/babel/babel/issues/5085#issuecomment-363242788 .bebelrc

{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ]
}


.babelrc

{
  "presets": [
    [
      "@babel/preset-env", {
        "targets": {
          "node": "current"
        }
      }
    ]
  ]
}

に変更した。 babel-preset-envのドキュメントによると
https://github.com/babel/babel/tree/master/packages/babel-preset-env

For convenience, you can use "node": "current" to only include the necessary polyfills and transforms for the Node.js version that you use to run Babel:

必要なポリフィルとトランスフォームを含めてbabelを実行してくれるらしい

node-lambdaをes2017で書くまでの道のり

目的

aws lambdaのデプロイなどができるパッケージnode-lambdaでasync/await(es2017)を書けるようになる
プログラミングモデル (Node.js) - AWS Lambda

AWS Lambda は、現在以下の Node.js ランタイムをサポートしています。
* Node.js ランタイム v6.10 (ランタイム = nodejs6.10)

とあるように aws lambdaのNode.jsサポート環境は2018/02現在で6系までとなっており、async functionに対応していない

開発環境

$ node -v
v8.9.3
$ npm -v
5.6.0

テストフレームワークはmochaを使う

Babel7を使う

babelのGitHubリポジトリはベータ版である7系で説明されており、webページだと6系をつかって説明されている。ここの差異ですごくハマった。
「@babel/hogeのようにインストールするのがナウい」と聞いて躍起になって7系を使おうとしてしまったが、冷静に考えたら6系でよかった。
なぜベータ版に必死になってしまったのか。
ということでbabel7系を使う場合はGitHubリポジトリを読みながら進めるのがよい。公式のDocやググった情報を参考にするとハマる(ハマった)。

package.jsonに入れるもの

サポートされている環境に基づいて必要なプラグインとポリフィルを自動的に決定しますとのことなので、babel-preset-envを入れる。
(es2017対応環境で使うとそのままのコードが吐き出されて、トランスパイルされてないと思って焦った。es2015対応環境で使えばちゃんとそこで動くようにトランスパイルされる。)

npm-scriptsを書く

必要なパッケージをインストールしたらnpm-scriptsを書く

  "scripts": {
    "test": "mocha --recursive",
    "compile": "babel src --out-dir compiled",
    "package": "node-lambda package -H /compiled/index.handler",
    "deploy": "node-lambda deploy -H /compiled/index.handler"
  },

各コマンドを説明する

  • "test": "mocha --recursive"
    mocha.optsでbabel-registerを指定することでes2017で書いたテストを実行できる。 test/mocha.opts
--require @babel/register

mocha.optsを書かずに、"test": "mocha --recursive --require @babel/register"としても可。

  • "compile": "babel src --out-dir compiled"
    これでsrc/に入っているコードをトランスパイルしてcompiled/に出力する。

  • "deploy": "node-lambda deploy -H /compiled/index.handler"
    これでトランスパイルしたコードをlambdaのハンドラに指定してデプロイする。
    npm run packageも同様。 https://github.com/motdotla/node-lambda#deploy

    -H, --handler [index.handler] Lambda Handler {index.handler}

まとめ

これでlambdaでasync/awaitを使うことができるようになった。 ポイントだなと思ったことはトランスパイルしたコードをlambdaのハンドラに指定すること。
今まで見てきたlambdaのディレクトリ構成がlambda/index.js,lambda/lib/lib.js(index.jsでインポートされている)みたいな構成だったので、
webpackいるのかと思って悩んだけど、babelだけで書けた。

package.json

  {
  "scripts": {
    "test": "mocha --recursive",
    "compile": "babel src --out-dir compiled",
    "package": "node-lambda package -H /compiled/index.handler",
    "deploy": "node-lambda deploy -H /compiled/index.handler"
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0-beta.39",
    "@babel/core": "^7.0.0-beta.39",
    "@babel/preset-env": "^7.0.0-beta.39",
    "@babel/register": "^7.0.0-beta.39",
    "chai": "^4.1.2",
    "mocha": "^5.0.0",
  }
}

create-react-appコマンドで作ったリポジトリをGitHubに上げる方法

ReactやVueなどのフレームワークcliから使う時、プロジェクトごと作ってくれる。

create-react-app my-app
create-react-native-app AwesomeProject
vue init webpack my-project

これをGitHub上にアップロードする方法をメモしておく。

  1. New repositoryでリポジトリを作る
  2. この時Initialize this repository with a READMEをチェックしない
  3. するとリポジトリを作った後に次に実行すべきコマンドが表示される
    …or push an existing repository from the command line にしたがいコマンドを実行する。 ローカルリポジトリを作ってリモートリポジトリを登録してからプッシュするという流れになる。

ローカルリポジトリで以下の手順を実行する。

createt-react-app my-app
cd my-app
git init
git remote add origin git@github.com:YOUR_USER_NAME/YOUR_REPOSITORY_NAME.git
git push -u origin master

コードを理解するときに個人的に気をつけているポイント

 

コードを読む時に気をつけているポイントの自分用メモ。(Githubを使っていることを想定)

 

ディレクトリ構成を理解する

ディレクトリ構成とファイル名を理解することでどんなプログラムか読み取ることができる。

フレームワークを使用している場合は、公式リファレンスにディレクトリの説明が書いてあるのでそれを読んで、ディレクトリの役割を理解する。

 

テストを読む

テストを読むことでインプットとアウトプットを理解する。

テストの文言からどのような事をおこなうプログラムなのか理解する。

 

コードを読む

ざっと関数だけを眺める。細かい実装は必要になったときに読む。

 

commitメッセージを読む

git blameを実行することで行毎のcommitメッセージを読むことができる。

git logを実行することでcommitログを新しい日付順に表示できる(デフォルトの場合)。

commitログからどのような変更か理解する。(Howの理解)

 

プルリクエストを読む

プルリクエストからどういった意図でプログラムが実装されているか理解する。(Whyの理解)