生産性向上ブログ

継続的な生産性向上を目指すエンジニアのためのブログ

Selenium ユーザー視点で Cypress を試したらめちゃくちゃ便利そうでした

この記事は、Selenium/Appium Advent Calendar 2017 の 23 日目です。

この記事では、ブラウザテストツールの Cypress の紹介を Selenium ユーザーである自分の視点から書きます。

Cypress とは

www.cypress.io

Cypress は、テストのセットアップ、作成、実行、デバッグなどをシンプルにするブラウザテストツールです。E2E テストを既存の Selenium のようなツールで実装・運用するときにありがちな辛い体験を改善して、開発者を幸せにすることが目的のようです。

Cypress は、3 年以上の間 private beta だったのですが、今年の 10 月に public beta になり、そのタイミングで大半が OSS となりました。

www.cypress.io

Cypress は、ThoughtWorks 社の Technology Radar の今年 11 月に更新されたバージョンの Tools 部門で ASSESS(調査する価値がある)として位置づけられています。

続きを読む

CircleCI 2.0 で Docker イメージをビルドして Docker Hub に push する

CircleCI 2.0 の小ネタです。

TL; DR

リポジトリのルートに Dockerfile がある前提です。

  1. CircleCI 2.0 で対象のリポジトリをプロジェクトとして有効にする
  2. CircleCI 2.0 上で環境変数に Docker Hub のユーザー名を DOCKER_USER に、パスワードを DOCKER_PASS に登録する
  3. .circleci/config.yml に以下のように記述する
version: 2
jobs:
  build:
    docker:
      - image: docker:17.07.0-ce-git
    steps:
      - checkout
      - setup_remote_docker
      - run:
          command: docker build -t miyajan/test-circleci-docker .
      - run:
          command: docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
      - run:
          command: docker push miyajan/test-circleci-docker

もう少し実用的に、セマンティックバージョンのタグ(1.0.1とか)が作成されたときだけ Docker イメージの build & push を行いたい場合は以下のようになります。

version: 2
jobs:
  buildh:
    docker:
      - image: docker:17.07.0-ce-git
    steps:
      - checkout
      - setup_remote_docker
      - run:
          command: docker build -t miyajan/test-circleci-docker:${CIRCLE_TAG} .
      - run:
          command: docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
      - run:
          command: docker push miyajan/test-circleci-docker:${CIRCLE_TAG}
workflows:
  version: 2
  build:
    jobs:
      - build:
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^[0-9]+\.[0-9]+\.[0-9]+$/

説明

CircleCI で Docker イメージの build を扱うための公式ドキュメントは以下にあります。

circleci.com

setup_remote_docker のステップを実行すると、Docker Engine 用のホストがリモートに立ち上がります。これ以降のステップの実行も image で指定されたメインのコンテナの中で実行されますが、docker コマンドは TCP 経由でリモートホストの Docker Engine を使用するようです。これは、メインのコンテナ内で docker コマンドの実行を許すと、いわゆる Docker in Docker のセキュリティ問題に直面するためこのような構成になっていると思われます。

なので、image に使用するイメージは、docker コマンドとチェックアウトのための git コマンドがあるものが望ましいと考えられます。幸いなことに、公式の Docker イメージ に git 入りのイメージ(*-git でタグが付いてるイメージ)が存在するので、それを使うのが楽です。

特定のタグのみでビルドが実行されるようにするためには、CircleCI の Workflow の機能を使う必要があります。

circleci.com

上記の公式ドキュメントに書いてありますが、タグのみでビルドが実行されるようにするための条件は少し複雑です。CircleCI 2.0 はデフォルトですべてのブランチでビルドが実行され、タグでは実行されないようになっています。なので、上記の例のように、すべてのブランチを ignore して、ビルドされてほしいタグ名を正規表現で記述するという2つの設定が必要になります。

まとめ

CircleCI 2.0 で Docker イメージをビルドして Docker Hub に push する方法と、それを特定のタグでのみ実行されるようにする方法について記述しました。

もう少し実用化を考えるとイメージのキャッシュを行って高速化したいところですが、ちょっと複雑になりそうなのでまたいつか気が向いたら書くことにします。

Jenkins Pipeline の when + branch は通常のパイプラインジョブでは使えない

Jenkins Pipeline についての小ネタメモです。

まず、when + branch の条件について簡単に説明しますと、ビルドが特定のブランチに対して実行されるときだけ処理を実行するように書ける文法です。

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

↑の例をマルチブランチパイプラインジョブで実行すると、production ブランチに対してトリガされたときだけ “Example Deploy” ステージが実行されます。

しかし、これを通常のパイプラインジョブで実行すると、ブランチに production を指定しても、

Stage 'Example Deploy' skipped due to when conditional

としてスキップされてしまいます。

不具合かと思って関連issueをウォッチしていたのですが、どうやらこれは仕様となったようです。公式ドキュメントにも明記されました。

branch
Execute the stage when the branch being built matches the branch pattern given, for example: when { branch ‘master’ }. Note that this only works on a multibranch Pipeline.

内部的に通常のパイプラインジョブとマルチパイプラインジョブでソースコード管理に依存しているプラグインが異なるのが原因なのですが、ユーザー側としては通常のパイプラインジョブでも同様に使えるほうが自然に思えるので、残念です。前回の記事で書いたように、通常のパイプラインジョブでもパラメータでブランチ名を受け取ってビルドのような用途は普通にありそうです。

一応、expression で git コマンドを叩いてブランチ名を比較するという回避策が issue のコメント上で紹介されていました。

when {
  expression {
    return sh(script: "git rev-parse --abbrev-ref HEAD", returnStdout: true).trim() == "master"
  }
}

ただ、手元で試したところ、通常だとブランチ指定子を設定しても Detached HEAD 扱いになるので、SCM の追加処理で “Check out to specific local branch” を設定しないとダメでした。(関連issue

通常のパイプラインジョブでブランチ名による分岐をしたいときは要注意です。