くりにっき

フルスタックキュアエンジニアです

gitlab_awesome_release v1.0.2をリリースした

https://gitlab.com/sue445/gitlab_awesome_release/blob/master/CHANGELOG.md#v102

Ruby 2.6.0で動かすとSEGVになるのでエラーにならないようにしました。

SEGVになる件は https://bugs.ruby-lang.org/ で報告してtrunkでは修正済なので、2.6.1では直っているはず。

bugs.ruby-lang.org

【追記あり】Travis CIでMJITを有効にしたビルドマトリクスを作る

何番煎じか分からないけど自分用メモ

tl;dr;

.travis.yml に下記のようなものを書くだけ

script:
  - RUBYOPT=$RSPEC_RUBYOPT bundle exec rspec

matrix:
  include:
    - rvm: 2.6
      env: RSPEC_RUBYOPT="--jit"
    - rvm: ruby-head
      env: RSPEC_RUBYOPT="--jit"

実行結果

https://travis-ci.org/sue445/rubicure/builds/473290010

f:id:sue445:20181229205640p:plain

ビルドスクリプト内で RUBYOPT="--jit" bundle exec rspec とかしてもいいんですが、それだとビルドがコケた時にJIT起因の問題なのかそうじゃないのかが分かりづらくなるのでこっちを推奨です。

あと、

rvm:
  - 2.6
  - ruby-head

を書いていた場合に、MJIT無しのビルドとMJIT有りのビルドが両方実行されて、MJITの有無でどれくらいビルドの時間が変わっているか比較しやすいというメリットもあります。

2019/6/19 追記

6/18くらいのCIからRuby 2.6系にも関わらず invalid option --jit というエラーが起きるようになりました

$ export RUBYOPT="--jit"

$ rvm use 2.6 --install --binary --fuzzy

ruby: invalid option --jit  (-h will show valid options) (RuntimeError)

https://travis-ci.org/sue445/omniauth-chatwork/jobs/547082488

いやいやそんなことないだろーと思ってたら、rspecの実行よりも前にエラーが起きているのでどうやらビルドの初期化のタイミングでTravisの中のRubyスクリプトが実行されていて、そいつがRuby 2.6未満なのが原因のようでした。

.travis.ymlRUBYOPT を上書きしないようにしたら --jit でコケないようになりました

github.com

俺的CircleCI Orbs開発ベストプラクティス

これは CircleCI Advent Calendar 201820日目です。

qiita.com

sue445/ruby-orbs を作って1ヶ月くらい経ちましたが、Orbをメンテしやすくするためのテクニックがいくつか分かってきたので紹介したいと思います

sue445.hatenablog.com

sue445.hatenablog.com

リリースごとにgitのtagを打つ

  • パッチバージョンのアップデート(+0.0.1)はmasterブランチへのコミットの度にリリース&tag pushする
  • メジャーバージョンのアップデート(+1.0.0)やマイナーバージョンのアップデート(+0.1.0)はローカルでgitのtagを作ってpushし、そこからCircleCI上でOrbをリリースする

この運用が自分の中で一番しっくりきています。

理由

  • バージョンごとにリポジトリにtagが打たれていると、GitHub上でのバージョン間の差分が見やすいため
  • パッチバージョンのアップデートは基本全自動で問題なさそうだが、メジャーバージョンやマイナーバージョンのアップデートは人間の意思が介在するのでローカルで作業をしたい(要出典)

後者に関してはローカルから circleci increment majorcircleci increment minor を叩いても実現はできます。

ただし自分の場合作業環境を複数台持っている(PCではなくサーバに入って作業することも多い)ので、各環境にcircleci cliトークンを置くよりはgitでtag pushしたら自動でそのバージョンのOrbをリリースできるようにするのがスマートだと思ったので実装しました。

実装方法

ソースはここにあります

masterブランチへのコミットの度に自動でOrbをリリース&tag pushする

関連するソースは下記です

version: 2.1

orbs:
  orb-tools: circleci/orb-tools@2.0.2

jobs:
  push_latest_version_tag:
    docker:
      - image: circleci/ruby:2.5
    working_directory: ~/app
    steps:
      - checkout
      - add_ssh_keys:
          fingerprints:
            - "43:39:3a:d1:90:49:a4:85:db:17:41:ce:88:55:44:92"
      - run: curl -fLSs https://circle.ci/cli | sudo bash
      - run:
          name: Setup git
          command: |
            git config push.default current
            git config user.email "$EMAIL"
            git config user.name "CircleCI"
      - run: ruby .circleci/push_latest_version_tag.rb sue445/ruby-orbs

workflows:
  version: 2

  btd:
    jobs:
      (略)

      - orb-tools/increment:
          orb-path: packed/orb.yml
          orb-ref: "sue445/ruby-orbs"
          segment: "patch"
          publish-token-variable: "$CIRCLECI_API_TOKEN"
          attach-workspace: true
          checkout: false
          requires: [orb-tools/test-in-builds]
          filters:
            branches:
              only: master

      - push_latest_version_tag:
          requires: [orb-tools/increment]
          filters:
            branches:
              only: master
            tags:
              ignore: /.*/

解説

  • orb-tools/increment でOrbのパッチバージョンを上げて、Orbリリース後にpush_latest_version_tag.rbを実行
    • ローカルからtagをpushした時にもincrementが走ると嫌なので、念の為 tags:ignore: /.*/ で明示的に除外しています。(なくてよかったかも)
  • push_latest_version_tag.rbhttps://github.com/sue445/circleci-ruby-orbs/blob/1.3.10/.circleci/push_latest_version_tag.rb を参照
    • Orb名を渡したら最新バージョンを取得してgitのtagを作ってpushしています
    • ビルドスクリプトなのでできればシェルでやりたかったのですが、標準出力を正規表現でparseするのが難しかったのでrubyに逃げました
    • 別途管理画面から Checkout SSH keysでuser keyを追加して、CircleCIのジョブからリポジトリにpushできるようにしてください

ローカルからtagをpushしたら自動でOrbをリリースする

関連するソースは下記です

workflows:
  version: 2

  release:
    jobs:
      - orb-tools/pack:
          source-dir: src/
          destination-orb-path: packed/orb.yml
          workspace-path: packed/orb.yml
          artifact-path: packed/orb.yml
          filters:
            tags:
              only: /^[0-9.]+\.0$/
            branches:
              ignore: /.*/

      - orb-tools/publish:
          orb-path: packed/orb.yml
          orb-ref: "sue445/ruby-orbs@${CIRCLE_TAG}"
          publish-token-variable: "$CIRCLECI_API_TOKEN"
          attach-workspace: true
          checkout: false
          requires: [orb-tools/pack]
          filters:
            tags:
              only: /^[0-9.]+\.0$/
            branches:
              ignore: /.*/

解説

tag pushに反応して $CIRCLE_TAG に対応したバージョンでOrbをリリースしています。

filters:branches:ignore: /.*/ で全てのbranchを明示的に除外しておかないと、ブランチpush時にもreleaseワークフローが実行され $CIRCLE_TAG が取れないのでエラーになります

fiters:tags:only: /^[0-9.]+\.0$/ のような正規表現にすることで、1.2.3 -> 1.2.4 のようなCircleCIからのパッチバージョンリリースでは実行されずに、1.2.3 -> 1.3.0 のようなローカルからのtag pushのみで実行されるようになります

この設定をしておかないと

  1. masterブランチをpush
  2. CircleCIでマイナーバージョンアップデート
  3. push_latest_version_tagでOrbの最新バージョンを取得してtag push
  4. 同じtagが作成済みなのでエラーになる
    • Orbのバージョンもtagもちゃんと作られているのに、コミットのビルドステータスが赤になるので気持ち悪い

ということになるので必要です。

ディレクトリ構成について

CircleCIのOrbは基本的に1つのyamlファイルです。

機能が少ないうちはいいのですが、機能が増えてくるとyamlファイルが巨大になって見通しが悪くなってしまいます。

      - orb-tools/pack:
          source-dir: src/
          destination-orb-path: packed/orb.yml
          workspace-path: packed/orb.yml
          artifact-path: packed/orb.yml

のように書くことで、src/にある階層化されたyamlファイルをいい感じに1つのyamlファイルにマージしてくれます。

説明すると長くなるので詳しくは下記PRを見てください。

https://github.com/sue445/circleci-ruby-orbs/pull/30/files

examplesを書く

Orbsのソースコードから実際の使い方を読み解くのは最初のうちは難しいです。GitHubみたいにREADMEがあればサンプルコードを書く余地があるのですが、Registryには(今のところ)そういう機能がありません。

しかし、examples を書くことでRegistryのUsage Examplesに表示させることができます。

Orbのexampleの記載

examples:
  bundle-install:
    description: |
      Run `bundle install` using cache.
    usage:
      jobs:
        rspec:
          docker:
          - image: circleci/ruby
          steps:
          - checkout
          - ruby-orbs/bundle-install:
              bundle_clean: true
              bundle_extra_args: ""
              bundle_gemfile: Gemfile
              bundle_jobs: 4
              bundle_path: vendor/bundle
              bundle_retry: 3
              cache_key_prefix: v1-bundle
              restore_bundled_with: true
          - run: bundle exec rspec
      orbs:
        ruby-orbs: sue445/ruby-orbs@1.3.0
      version: 2.1

Registryでの表示

https://circleci.com/orbs/registry/orb/sue445/ruby-orbs#usage-bundle-install

f:id:sue445:20181208225055p:plain

Orbsの書き方の学習方法

公式ドキュメントはこちらになります https://circleci.com/docs/2.0/creating-orbs/

しかしドキュメント化されてない機能もある*1ため、公式のOrbsのソースを読むのが一番です。

公式Orbsのソースは https://github.com/CircleCI-Public 配下のリポジトリにまとまっているのですが、僕は普段下記のリポジトリを参考にしています。

*1:自分が見つけきれていない説もある

プリキュアボットを支える技術

これは プリキュア Advent Calendar 2018 の16日目です。

adventar.org

はじめに

今日はキュアピースの中の人の金元寿子さんのお誕生日です。おめでとうございます!!!!

嫁集合写真

キュアスタ!とは

Mastodonプリキュアインスタンスです precure.ml

キュアスタ!ではサーバに負荷をかけない程度であればボットの作成は容認されています。*1

いくつかボットを作ってるのでその紹介です

プリキュアこのあとすぐボット

プリキュアが始まる直前にトゥートするボットです。

https://precure.ml/@precure_onair_bot

f:id:sue445:20181130202050p:plain

元ネタ

最初作った時は開始10分前だけの通知でしたが、1日1回まとめて通知できた方が便利そうだったので0時にその日に放送される番組をまとめて通知するようにもしました。便利

f:id:sue445:20181130202141p:plain

今まで作った中ではこれが一番のお気に入りです

使っている技術

プリキュア誕生日ボット

プリキュアの誕生日の日にトゥートするボットです

f:id:sue445:20181130202239p:plain

https://precure.ml/@precure_birthday_bot

使っている技術

プリキュア つながるぱずるん」非公式

キュアぱず公式アカウント のツイートをきゅあったーに流すボットです。

f:id:sue445:20181130202644p:plain

https://precure.ml/@precure_app

使ってる技術

拙作の twittodon で流しています

詳しいこと

sue445.hatenablog.com

CIマニアから見た各種CIツールの使い所

社内外でちょいちょい聞かれるのでメモ。

前置き

  • 100%自分の主観なので偏ってます
    • SaaSかオンプレならSaaS派。(自分でサーバの面倒身たくない)
  • 自分が使ったことがないものは紹介していません
    • 今回紹介してるTravis CI, CircleCI, Wercker, GitLab CI, Jenkinsに関しては仕事や趣味で各3〜4年くらいは使ってるはず

GitHubを使ってる場合

ライブラリを作ってる場合

紛らわしいですが https://travis-ci.org/ は無料版で https://travis-ci.com/ が有料版。

今は両方 https://travis-ci.com/ に統一されてるとのこと *1

OSSであれば基本的にはTravis CIを使っていればいいと思います。

Travis CIを選択する理由

2020/4/21追記

CircleCIでマトリクスビルドがサポートされたので一部修正

Travis CIを選択しない理由

アプリを作ってる場合

アプリの場合

  1. テスト
  2. テストが成功したもののみ開発環境にデプロイ
  3. 開発環境で確認して問題がなかったらステージング環境にデプロイ
  4. ステージング環境にデプロイして問題なかったら本番環境にデプロイ

というワークフローがよくあると思いますが、Travis CIだと複雑なワークフローを組むのが大変なので CircleCIWercker を使うのがいいと思っています。

CircleCIとWerckerの共通点

  • 任意のDockerイメージが使える
  • ワークフローが組める
  • 設定ファイルがymlファイル
  • 無料プランでもプライベートリポジトリがビルドできる

CircleCIとWerckerの機能差異

CircleCI 1.0時代はWerckerの方が機能面で優秀でしたが、現時点ではCircleCIの方が全体的に使い勝手がいいと思っています。(今後変わるかも)

理由

  • Werckerは1リポジトリ1 Dockerイメージだが、CircleCIはジョブ単位で利用するDockerイメージを変えられる
  • CircleCIはスケジューラ(cronのようにジョブを定期実行)をサポートしているが、Werckerは非サポート
    • WerckerのAPIをcrontabから定期的に叩けばいけるんだけど、CIサービスだけで完結しないのが難点
    • 【宣伝】Werckerでジョブを定期実行しやすくするツールは昔作りました
  • CircleCIはほぼ全ての設定がymlに集約されているが、Werckerの場合ワークフローの定義部分は管理画面で設定する必要がある
    • リポジトリの管理者しかワークフローの設定をいじれないので、第三者がワークフローの設定変更のPRを送りづらい
  • CircleCIとWerckerではCircleCIの方が複雑なワークフローを組める
  • CircleCIだと自動実行だけでなく、人間が承認することによってワークフローの先のステップに進めるということができる(Manual Approval)

GitLabを使ってる場合

リポジトリ(GitLab本体)と一体化していて連携が楽なのでGitLab CI一択。

設定ファイルの書き方に細かい違いはあるものの、基本的にCircrleCI 2.1相当の機能があるので普通に使う分にはまず機能面で困ることは無いと思います。

GitLab CIの優位点

Jenkinsなどを使った方がいい場合

下記のユースケースに関してはJenkinsや他のツールを使った方がいいと思います

  • 複数のリポジトリにまたがるジョブを構築したい時
    • Travis CI・CircleCI・GitLab CI・Wercker、いずれにせよ複数リポジトリで連携させるジョブを作るのは難しい
  • Parameterized Trigger Plugin のように、ジョブ実行時にパラメータを選択したい時
  • ビルド環境がハードウェア依存の場合
    • 一番分かりやすいのはiOS/Android/Unityアプリなどのビルド
      • ただしCircleCIはiOSビルドをサポートしているし*4、GitLab CIでもMacをRunnerにすることでiOSアプリをビルドすることもできるので、「ハードウェア依存のビルドだからJenkins必須」とは一概には言えない
  • イントラ環境でのビルドが必要な場合
    • SaaSを使いたくない(使えない)という大人の事情以外にも、イントラ内じゃないとライブラリやパッケージがダウンロードできないというのはある
      • ただしそういう時でもCircleCIのEnterpriseを利用するとか、GitLab CIのRunnerをオンプレに置くという方法はある*5

追記:2018/12/8

from id:tnir

CIマニアから見た各種CIツールの使い所 - くりにっき

GitHub\.comを使ってる場合、GitLab CI/CD for GitHub (SaaS)の選択肢はないのかどうか。

2018/12/08 00:01
b.hatena.ne.jp

なるほど。これは知らなかったです

about.gitlab.com

アリかナシかで言えばアリで、GitHubだけどイントラ内でCIをしたい(が自前でJenkinsをホスティングしたくもない)ような場合にはGitLab CIに軍配が上がりそうです。(Runnerをホスティングする手間はあるけど)

*1: https://twitter.com/ndxbn/status/1071045198145191943

*2:TravisだとOracleのインストールだけで10分くらいかかった

*3: https://travis-ci.com/plans

*4:https://circleci.com/docs/2.0/ios-tutorial/

*5:GitLabはそもそもオンプレやんというツッコミはありそうですが、SaaSのGitLab.comとオンプレのRunnerを連携できます

Pixelaでesaのkusaを生やす

esaとkusa(草)の語感が似ていたのでやってみました (\( ⁰⊖⁰)/)

やり方

Pixela の設定

以下は拙作の pixela gemを使っていますが、他言語の場合は適当に置き換えてください

require "pixela"

client = Pixela::Client.new(username: "YOUR_NAME", token: "YOUR_TOKEN")

# Pixelaにユーザ登録していないなら登録する
client.create_user(agree_terms_of_service: true, not_minor: true)

# グラフを作成する
# colorはshibafu, momiji, sora, ichou, ajisai, kuroのいずれかを選択
# c.f. https://docs.pixe.la/#/post-graph
client.create_graph(graph_id: "esa", name: "esa activity", unit: "kusa", type: "int", color: "shibafu", timezone: "Asia/Tokyo")

# webhookを作成
client.create_webhook(graph_id: "esa", type: "increment")
#=> #<Hashie::Mash isSuccess=true message="Success." webhookHash="XXXXXXXXXXXXXXXX">

create_webhook の戻り値で返ってくる webhookHash は後で使うので忘れないようにしてください

esa の設定

Ownerアカウントで「チーム情報 -> Webhook・アプリ連携」よりGeneric Webhookを登録

f:id:sue445:20181206001624p:plain

URLは https://pixe.la/v1/users/【ユーザ名】/webhooks/【webhookHash】 の形式

チェックボックスはお好みで。

実際のグラフ

https://pixe.la/v1/users/【ユーザ名】/graphs/【グラフID】svghttps://pixe.la/v1/users/【ユーザ名】/graphs/【グラフID】.html がhtmlのURLです。

esaに埋め込む方法

こんな感じ

[![esa activity](https://pixe.la/v1/users/【ユーザ名】/graphs/【グラフID】)](https://pixe.la/v1/users/【ユーザ名】/graphs/【グラフID】.html)

サンプル

## esa activity
[![esa activity](https://pixe.la/v1/users/sue445/graphs/esa)](https://pixe.la/v1/users/sue445/graphs/esa.html)

で埋め込んだ図が下記です。READMEに埋め込んでおくと分かりやすいでしょう。

f:id:sue445:20181206000114p:plain

注意点

esaはログインしないと見れませんが、Pixelaのグラフ(草)はログイン無しで見れてしまいます。

そのため、もしPixelaのグラフを作る場合はURLを第三者に知られないようにし、リスク(esaのactivityがpublicになる)を認識して利用してください。

rubocop_auto_correctorを作った

はじめに

これはアドベントカレンダーとは全く関係ないいつもの技術エントリです

github.com

モチベーション

僕は普段rubocopでauto correctする時はコミットの可読性の観点から下記のように各cop単位でauto correctしてコミットするようにしています。

sue445.hatenablog.com

このスクリプトは非常に便利なのですが、個人dotfilesにあるため他の人に使ってもらいにくいという難点がありました。

先日のOSSパッチ会 でrubocopコミッタの id:koic に「こういうスクリプトってrubocop本体にあるとどうっすかね?」みたいな感じで雑に相談したら、「本体よりも別のツールとしてあった方がよさそう」という返事をもらったのでgem化したのが今回の rubocop_auto_corrector になります

使い方

bundle exec rubocop_auto_corrector で実行するだけです

実行結果のサンプル

https://github.com/sue445/rubocop_auto_corrector/pull/3/commits

f:id:sue445:20181130235925p:plain

gem化するにあたって気をつけたこと

ユニット(?)テストの整備

gemの性質上実際にgit commitするところまでテストで検証する必要があったので、rspecの中で

  1. ダミーアプリをgit init
  2. rubocop_auto_correctorを動かして rubocop --auto-correct && git commit
  3. gitのコミットメッセージが意図通りか検証

のようなことを行っています

詳しくはこの辺のソースを読んでください

auto correctできないcopはスキップするようにした

今まではrubocopの実行結果に出てきたcop全部 rubocop --auto-correct してたので割と無駄が多かったのですが、それは無駄なので実際にauto correctできるcopのみrubocop --auto-correct するようにしました。

しかし「言うは易く行うは難し」というやつで、cop名からcopのクラス名とgem名(プラグインの場合)を取り出す方法が色々と面倒くさかったです。。。(特にrubocop-rspec

https://github.com/sue445/rubocop_auto_corrector/blob/v0.1.0/lib/rubocop_auto_corrector/cop_finder.rb#L41-L54