RailsプロジェクトのCircleCI2.0設定ファイルを定義した

自分のサンドボックス用プロジェクトのCircleCIを2.0移行したのでそのメモ。

今回の内容は下記のthoughtbotの記事を参考にすれば動作するはずだし、必要なことは書かれている。 robots.thoughtbot.com

作業したプルリクは以下

Add config file for CirclrCI 2.0 by kuranari · Pull Request #17 · kuranari/mblog · GitHub

Docker image

imageはcircleci/ruby:2.5.0-node を使用した。

circleci/ruby:2.5.0ではないのは、nodeの実行環境が必要なRubyのGem uglifierに依存しているため。

他コンテナの起動を待つ

テスト用のコンテナよりもDBコンテナの起動に時間がかかり、DBへの接続が出来ずエラーになってしまうことがある。(このようなエラーとなる。)

そのためdbの起動をdockerizeコマンドで待つようにした。

Add config file for CirclrCI 2.0 by kuranari · Pull Request #17 · kuranari/mblog · GitHub

thoughtbotのブログの設定ファイルにもその旨書かれているが自分の携わる他プロジェクトでは記述がなかったため当初dockerizeをしていなかった。 そのプロジェクトではテスト用コンテナでミドルウエアやnpmのインストールのような多少時間がかかる処理が発生するため、MySQLコンテナの起動待ちとなる状況は発生しないようである。

workflow化

現状のCIで行うことはテスト実行のため実質不要だが、興味があったのでworkflow化をしておいた。 workflowを使うと、複数のタスクを並列に実行させたり、直列に定義された各Jobの実行有無を条件によって切り分けられる。

並列実行の例でいうとRuby2.3, 2.4, 2.5の3バージョンを対象に並列でテストができるようになる。 直列実行の例だとbuild, test, deployの3つのJobがある場合、featureブランチではbuild, testまでを行い、masterブランチでのみbuid, test, deployを行うことができる。

またworkflowを定義することでcronのようなスケジュール実行を行うことができる。

この辺は下記の記事が詳しい。 qiita.com

まとめ

RailsプロジェクトのCircleCI2.0設定ファイルの記述についてまとめた。 今回はシンプルにrspecを実行するだけの設定にとどまっているが、今後デプロイ設定なども試していきたい。

Maven + Lombokを使うまでのメモ

Mavenで管理しているプロジェクトでLombokを使うための設定方法のメモです。 この記事では

の2つの手順を踏んでいます。 なおIntelliJなどのIDEでLombokを使うためには別途設定が必要になります。

事前条件

javaとmavenがインストールされていること。

% java -version
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)

% mvn --version
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T03:33:14+09:00)
Maven home: /usr/local/Cellar/maven/3.5.4/libexec
Java version: 1.8.0_162, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "mac os x", version: "10.13.4", arch: "x86_64", family: "mac

Mavenプロジェクトの作成

% mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.743 s
[INFO] Finished at: 2018-08-13T15:53:17+09:00
[INFO] ------------------------------------------------------------------------

*Mavenのインストール直後だと時間がかかる。

生成されたファイルは以下の通り。

% tree
.
└── my-app
    ├── pom.xml
    └── src
        ├── main
        │   └── java
        │       └── com
        │           └── mycompany
        │               └── app
        │                   └── App.java
        └── test
            └── java
                └── com
                    └── mycompany
                        └── app
                            └── AppTest.java

12 directories, 3 files

Mavenのテンプレートをビルドする

% cd my-app
% mvn package
[INFO] Scanning for projects...
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.509 s
[INFO] Finished at: 2018-08-13T15:59:04+09:00
[INFO] ------------------------------------------------------------------------

コンパイルした実行ファイルの起動

% java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Hello World!

ここまででMavenプロジェクトの作成は終了です。 続いてlombokの設定を行っていきます。

POM.xmlの編集

% vi pom.xml

下記の依存関係を追加

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

App.javaでlambokのアノテーションを仕様

% cat src/main/java/com/mycompany/app/App.java
package com.mycompany.app;

import lombok.Data;
import lombok.val;

@Data
class User {
    private final int id;
    private final String name;
}

public class App
{
    public static void main( String[] args )
    {
        val user = new User(1, "Alice");
        System.out.println(user);
    }
}

再実行

mvn package
% java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
User(id=1, name=Alice)

FluxのRuby実装をした

この記事は2016年11月21日に社内ブログに掲載したものの公開版となります。

ソースコードは以下のリポジトリにアップロードしています。

github.com

Flux概要

  • Facebookが提唱したUI構築用のアーキテクチャ

    • 特定のライブラリや実装ではない
  • Reactとの併用が推奨されている

    • React.jsはMVCモデルのV(View)の部分の役割に特化している
  • データが単方向に流れる f:id:kuranari_tm:20171215233654p:plain

fluxのメリット

イベント発生に合わせてどんな処理をどこの部品として実装すべきかが明確 → コードの見通しが良くなる

非同期イベントに伴うデータの流れが明確になる → デバッグが容易になる

各部品は疎結合かつ入出力が明確 → 再利用性やテスト容易性を確保できる

Rubyで実装してみる

  • アーキテクチャであれば言語やフレームワークに依存しないはず
  • 作るもの: CUIのカウンター
    • 1ファイルにまとめると以下のようなコード
    • これぐらいの規模だとわざわざfluxを採用する意味はないが、学習用として目をつぶる
count = 0
print 'input: '
while op = gets.chomp
  case op
  when '+'
    count += 1
  when '-'
    count -= 1
  end
  puts count
  print 'input: '
end

リポジトリ

https://github.com/kuranari/flux-ruby

EventEmitter

  • いわゆるオブザーバーパターン
  • イベントを登録しておくと、特定のイベントが発火したときにコールバックが実行される
class EventEmitter
  def initialize
    @handlers = Hash.new { |h, k| h[k] = [] }
  end

  def on(type, &handler)
    @handlers[type] << handler
  end

  def off(type)
    @handlers[type].clear
  end

  def emit(type, data = nil)
    @handlers[type].each do |handler|
      handler.call(data)
    end
  end
end
require './event_emitter'

event_emitter = EventEmitter.new

event_emitter.on('click') do
  puts 'hello world'
end

event_emitter.on('input') do |params|
  puts params
end

event_emitter.emit('click')
# => hello world

event_emitter.emit('input', 42)
# => 42

Dispatcher

Action → Dispatcher → Store ActionをStoreに配送する。

実装

EventEmitterそのものをDispatcherとして使用。 実際に使う場合は実行順序の制御機構などもう少し複雑になる。

require './event_emitter'

class Dispatcher < EventEmitter
end

Store

Dispatcher → Store → View - MVCでいうModelに相当。 - Storeが管理するデータはStore自身のみが更新できる - Storeの状態に変更があった場合EventEmitter経由でStoreに通知

require './event_emitter'

class Store < EventEmitter
  attr_reader :count

  def initialize(dispatcher)
    super()
    @count = 0
    dispatcher.on('UPDATE_COUNTER') do |payload|
      on_update_counter(payload[:value])
    end
  end

  private

  def on_update_counter(count)
    @count += count
    emit('CHANGE')
  end
end

ActionCreator

View → Action → Dispatcher - MVCでいうControllerに相当

Dispatcherに対しActionを作成して配信する。

Actionは

  • UPDATE_COUNTERのようなアクションの識別子
  • {value: 1}など入力値(パラメータ)に相当するオブジェクト

のペアで構成される。

class ActionCreator
  def initialize(dispatcher)
    @dispatcher = dispatcher
  end

  def increment_counter
    @dispatcher.emit('UPDATE_COUNTER', value: 1)
  end

  def decrement_counter
    @dispatcher.emit('UPDATE_COUNTER', value: -1)
  end
end

View(Component)

Store → View → Action

  • ユーザーイベントの受付
    • clickされたら◯◯するといった動作
    • イベントハンドラで対応するActionを呼ぶ
  • Storeの変更を検知して再描画

簡易版ReactComponent

  • state=で状態が変わったら再度renderする。

雑に実装すると、ReactComponentがやってることは以下のようなことだと思う。

class ReactComponent
  def initialize
    @state = {}
  end

  # stateに変更があったら再描画
  def state=(state)
    @state = state
    render
  end

  def render
    raise NotImplementedError.new
  end
end
class Component < ReactComponent
  def initialize(store, action)
    @store = store
    @action = action

    @state = { count: @store.count }
    @store.on('CHANGE') do
      on_change
    end
  end

  def increment
    @action.increment_counter
  end

  def decrement
    @action.decrement_counter
  end

  private

  def on_change
    self.state = { count: @store.count }
  end

  def render
    puts "count: #{@state[:count]}"
  end
end

Main

require './event_emitter'
require './action_creator'
require './store'
require './component'

dispatcher = EventEmitter.new
store = Store.new(dispatcher)
action = ActionCreator.new(dispatcher)
component = Component.new(store, action)

print 'input: '
while command = gets.chomp
  case command
  when '+'
    component.increment
  when '-'
    component.decrement
  end
  print 'input: '
end

イベントの末端にスタックトレースを仕込んでみる

Component#on_change

まとめ

  • Store, Action, Viewを各々EventEmitter(Dispatcher)で監視することでユーザーの入力から画面表示までの1サイクルが回ることを確認した。
  • fluxを理解するには実装するのが一番早いと思う(普通にjsで実装すればいいと思うが…)

参考文献

SENDAI IT COMMUNE meetup #01に参加してきた

SENDAI IT COMMUNE meetup #01

techplay.jp

モチベーション

東北出身のエンジニアとして将来的に東北で仕事をする可能性があるということで、イベントに参加してきました。 東京にいるとなかなか東北の企業事情が入ってこないので、このようなイベントの開催はとても助かります。

イベントは想像以上に熱量が高く、記事に書いていいものか怪しいほどぶっちゃけた話が聞けたのですが、とりあえず書ける範囲でメモを公開します。

メモ

トークテーマ: 『仙台のIT産業を盛り上げるためには -- ぶっちゃけ、仙台・宮城のIT産業ってどうなの?』

  • 仙台のIT企業: データ上は400社くらいある
    • ただし殆どが受託と思われる
    • 交通や電力会社の受託を行っている会社が多い
  • 自社サービスを行っている企業

  • 昔に比べて少しずつ状況は変わってきている

    • プレイヤーの世代交代が進んできた
    • 昔は(今も?)受託の提案案件で負けると恨みを買うようなこともあった
    • 商工会議所のような組織はあるが、競争力の源泉にはなりえない
    • 逆に関東で揉まれて力をつけてから仙台に乗り込むとチャンスはある
    • 助成金だよりではなく自走する組織を目指している(少なくとも今回参加している企業は!!)
  • 仙台のITコミュニティ / 社会人教育

    • 仙台の勉強会はほぼゼロ(あることにはあるが質はあまり高くない)
    • セミナーを開催し、広告を打っても参加者が殆ど集まらないこともあった
    • ただし仙台(東北)だから人が集まらないなんてことは無く、本気でやれば石巻でも人を呼べる
      • 石巻に80人の学生を読んでイベントの開催も出来た
    • 勉強会参加のために社員を東京に出している企業も多い
    • 外の世界と自分がどれくらい離れているかを知ることが出来ないのが仙台のデメリット
    • 国分町1丁目にコワーキングスペースができるなどいい兆しも出てきている
    • 個人投資家は居ないが、融資は受けやすい

次回

いい部分だけでなく、負の話が思いっきり聞けるので東北・仙台で働くことを考えているIT関係者は一度参加してみるといいと思います。 2018/1/20に仙台で開催とのことです! techplay.jp

jsでEncrypted Secretsライクなライブラリを実装した

Webアプリケーションを開発していく中でAPIキーやパスワードなどの機密情報をどのように管理していくかは常に頭を悩ます課題となっています。 これまで機密情報は環境変数を使用する、設定ファイルをGit管理しないなどの方法を取ることが多かったのではないでしょうか。

機密情報の管理方法として、Ruby on Railsではバージョン5.1からEncrypted secretsという解決策を打ち出しています。 Encrypted secretsは機密情報を含むファイルを暗号化した状態でバージョン管理システムにコミットし、復号化のためのキーのみを環境変数やVCS管理外のファイルで保存する方法を取ります。

Rails 5.1: Loving JavaScript, System Tests, Encrypted Secrets, and more | Riding Rails

暗号化をしているとは言え機密ファイルをGit管理するのはよくないケースはあるのかもしれません*1が、殆どのケースでは「なんで今までこれを採用しなかったのか」と思うほどの便利に使っています。

今回はRailsで便利と感じたEncrypted secretsの仕組みをReact Nativeで使用することを見据えてjs実装してみました。 リリースノートを見るとこの機能はsekretsというGemを参考に開発を進められたようなので、私もsekretsのコードを参考にしています。

github.com

成果物

Githubにあげていますが、現状Productionでは使用していません。 標準入力を取得する部分でOS依存のコードとなっているためWindowsでは動作しないです。

GitHub - kuranari/js-secrets: js-secrets is command line tool and library used to securely manage encrypted files and settings in your javascript applications and git repositories.

INSTALL

$ npm install -g js-secrets

How to use

setup

$ js-secrets setup
created: .secrets.key
created: secrets.yml.enc

js-secrets setupでkeyファイル.secrets.keyと暗号化された機密ファイルsecrets.yml.encが生成されます。

read

$ js-secrets read
awesomeValue: 42

js-secrets readで機密ファイルを復号化し、標準出力に表示します。

write

$ echo 'hello: world' | js-secrets write
$ js-secrets read
hello: world

標準入力を暗号化し、secrets.yml.encに保存します。

edit

$ EDITOR=vi js-secrets edit

環境変数(EDITOR)で指定したエディタで機密ファイルを編集します。 編集後は自動で暗号化されsecrets.yml.encファイルに保存されます。

.secrets.keyはgitignoreに追加する必要があります。

ファイルからの読み込み

const { load, fetchKey } = require('js-secrets');

const settings = load('./secrets.yml.enc', fetchKey({ path: './.secrets.key' }))
console.log(settings);

感想

  • sekretsが.gitignoreの更新や、keyファイルの探索、Capistranoのタスクを含んでいるなど、細かいところに気の利いたライブラリと感じました。
  • 自分のライブラリ方はと言うと、コマンドラインオプションへの対応やら、jsファイルで.encファイルや.keyファイルを明示しなければならないなど自分自信で「このライブラリのAPIイケてないな」と思う部分も多いです。
  • READMEが圧倒的に足りなく、OSSとして普及させるための技術以外のハードルがあるなと思っています
  • とは言え、飽きる前に一通りの機能を実装してソースとブログ公開ができたのは技術的に一歩踏みだせた感があります🎉

*1:「本番環境のDBのパスワードは特定の人しか閲覧できないようにする」等

Sekretsで使われていたRubyの組み込みメソッド

Sekretsのコードリーディングを行っていた際に、これまで知らなかったメソッドを発見したため、そのまとめ。 GitHub - ahoward/sekrets: sekrets is a command line tool and library used to securely manage encrypted files and settings in your rails' applications and git repositories.

Kernel.#at_exit

https://docs.ruby-lang.org/ja/latest/method/Kernel/m/at_exit.html

Kernel.#test

https://docs.ruby-lang.org/ja/latest/method/Kernel/m/test.html

Process::Status($?)

https://docs.ruby-lang.org/ja/latest/class/Process=3a=3aStatus.html

builderscon tokyo 2017に参加してきた[day 3]

ここまで出来るmruby

builderscon.io

  • CRubyとの違い
    • RegexpやFileなどが拡張扱い
    • mrbgemで拡張(エムジェム)
  • 省メモリを生かして組込み用途へ
    • mod_mruby
    • ngx_mruby
  • mrubyはLLのもう一歩先へ進める言語
    • mrubyを活かすなら低レイヤー
    • mrubyはソースコードが追いやすい
      • build_config,rb
        • rubyのDSL
      • mruby
      • ctagsを使うと言語を超えてdebugできる
      • mrbgemのdebugはmrubyとのつながりが強い
  • 深く理解するということ
    • gdbデバッグ、Kernel, Craiburarino 周辺知識が得られる
    • ミドルウエアやOSのボトルネックやパラメータに迫れるようになる
    • Internetより俺のほうが詳しい体験
      • mrubyはソースを読む
      • C言語やmiddlewareのレイヤーはドキュメントを読む
  • mrbgem化されていないライブラリ
  • opennssl, thor, faraday
  • まとめ
    • mrubyを利用することで低レイヤーに強くなる
    • middleware + mruby = 強い
      • 他社に真似出来ない仕組みが作れる

小さく始めて育てるコンパイラ

  • オリジナルの言語を作るには

      1. 既存言語のサブセットとして最低限の機能を実装
      1. 欲しい機能を拡張として実装
      1. オリジナルの構文に置き換える
    • → やらなければいけないことが
  • なぜ既存言語のサブセットとしてまずは実装するのか

    • 構文のデザインを先にやってしまい、つまづくことが多い
    • どういう構文がいいのかは実際に書いて動かさないと分からない
    • 実装に詰まったときに既存言語を参考にできる
  • GoCamlはMinCamlをベースに作った

    • MinCamlは教育用言語なので、シンプル
    • バックエンドはLLVM
  • 型推論期が多層的な型を推論できるように拡張

    • LLVM IRには多相型はない

builderscon.io

ランチセッション

  • エンジニアがkintoneを使うべき3つの理由 サイボウズ株式会社
  • 検索サービス開発が絶対におもしろいと思う理由 Supership株式会社

Make you a React: How to build your own JavaScript framework.

  • hyperapp
  • picodom
  • hyperappのソースを読んだことがあったため、メモ少なめ
  • セッション後hyperapppicodomの今後の開発方針についてお話をさせていただいた
    • hyperappは無依存のフレームワークとして
    • picodomは他のフレームワークのVirtual DOMエンジンとしての役割を持たせていくとのこと。

builderscon.io

Factory Class

builderscon.io

  • キーボードを中国の企業で量産する際の話
  • 工場選びで再重要なこと: ボスと直接ランチを取れるような関係と付き合うこと
    • そうでなければ、自分たちが小さすぎる
  • 工場の見学は必須である
    • 中国では人と人とのつながりが重要
    • 契約は重要ではない
    • お茶の儀式が終わるまではビジネスの話はしない
    • リリースされていない製品の写真を出してくることがある
      • 主要なNDA違反になっているんだろうなと思って見ていた
  • 各種書類を揃えておいたほうがよい
    • Bill of Materials(BOM)
      • 最も大切な資料
      • それぞれの部品に対して詳細な仕様を決める
      • どこから調達するかも記載する(1社からしか調達できないのか、一般品なのか)
      • Dragon Standard BOMというテンプレートがよい
    • Electrical design files
      • ODMならデザインをしてくれる
      • 今回は自分たちで作った(コントロール・フリークだった)
    • Machanical design files
      • CADなどで使用
      • 2D図面図も作成
    • Product Requirements
      • なぜこの製品を作っているのか
      • SWは誰が書くのか
      • どうテストするのか
      • → 工場では開いてはくれないが、自分たちの方針付けに重要な資料になる
    • Manufacturer Survery
      • 工場に対するアンケート
      • 工場のサイズは大丈夫か
      • 財務状況は大丈夫か
      • 児童労働をしていないか
  • 先方からの見積もりも、詳細度が重要
    • 安すぎる場合何かが怪しい場合がある

The Evolution of PHP at Slack HQ

builderscon.io

  • 現在のslackの社員
    • 社員: 約1, 000名
    • エンジニア: 約400名

PHP

コアの部分にPHPを採用している

  • PHPは誰も使いたがらない

    • しかし、影響力の強いWebサービスはPHPで書かれている
    • Facebook, wikipedia, Wordpress, Flickr, etc…
  • なぜslack社ではPHPを使い続けているのか

    • FounderがPHPを使っているから
    • 既存のPHP資産を流用するため
      • Hackを使うことによって、後方互換性を保ったまま不具合点を改善できる