TECH-MICCHON.jar

Scalaを中心に技術的な話題を書きます。

OSS のメンテナになるということ

皆さんはじめまして。 FOLIO でエンジニアとしてインターンしているみっちょんです。普段は大学で Tracing JIT Compiler の研究をしています。

本記事はFOLIO Advend Calendar 10日目のものです。 昨日は matsu_chara さんで「Application Crash Consistency and Performance with CCFSを読んだ」でした。

scalaenv という OSS のメンテナになってからやってきたこと、そしてやるべきこと

scalaenv とは

scalaenv とは、Scalaのバージョンマネージャです。

github.com

Ruby には rbenv があり Python には pyenv があるように Scala にも同様のツールがあります。

自分がこのプロダクトにいくつか Pull Request を送っていたのと、作者さまと Twitter でつながっていたという縁もあり、2017年の夏くらいにメンテナになりました。

以降では、 scalaenv のメンテナになってからやってきたこと、そしてメンテナとしてやるべきことを書いていこうと思います。

OSSメンテナとは、命をつなぐ者である

メンテナとはOSSの維持・管理を行っていく人間のことで、言ってしまえば OSS の生命維持装置に他なりません。

メンテナがいなければそのプロダクトは緩やかな死を迎えていきます。

人や組織からお金をもらってメンテナになる場合もありますが、世の中にある殆どのOSSは有志によって維持・管理がなされています。 そして、メンテナはOSSに対して責任を持ち、開発・運用していきます。

メンテナとしてやってきたこと、そしてやるべきこと

OSSのメンテナとして最低限すべきであると思った作業は以下の三つです。

  • issue の整理
  • Pull Request のレビュー
  • 定期的なリリース

issueの整理

issue は往々にして溜まっていくものですが、それを放置しておくのはプロダクトにとっての停滞を意味します。

したがって、 OSS メンテナは最初にどんな issue が寄せられているのかチェックし、可能であればその場で対応していくのが望ましいです。

例えば、 scalaenv には

のような重要な要望が寄せられていました。そういった issue を分類し、優先順位を付け、可能であればマイルストーンを付けて対応していくのがメンテナの役割です。

幸い、当時はまだ時間が余っていたので、いずれの issue も解決し closed な状態にできました(めでたい)。

ただ、自分が立てた issue でまだ対応出来ていないものがあるので、それを解消していくのがこれからの目標です。

Pull Request のレビュー

Pull Request が放置されているのは OSS の利用者にとって好ましい状況ではありません。

したがって、 OSS のメンテナになったらPull Request のレビューも issue の整理と同様に重要な作業です。

scalaenv のメンテナになってからはほぼ自分が修正点をコミットしていったので、まだ一つしか Pull Request を受け取っていませんが、溜めている Pull Request は一つもないという状態を維持しています。

定期的なリリース

OSS の利用者にとって、定期的なリリースが行われ、常に改善がなされていることは良いことだと考えます。

なぜなら、アクティブなコミッタがその OSS にいることを意味しますし、仮に自分がバグを見つけたり機能追加のアイディアを思いついた場合、コミッタに報告すればすぐ対応してくれるだろうという見込みが立てやすくなるからです。

アクティブにコミットする

つまり、 OSS メンテナはある程度アクティブに開発しているというアピールをしなければなりません。

私がメンテナになった時、開発はやや停滞気味であったので、まずは目先の問題を解消していこうと考えました。その結果、

  • 機能追加 / バグ修正
  • 定期的なリリース

を適宜行っていけば利用者はきっと関心をもってくれるだろうというところに落ち着き、 OSS 利用者にとって意味のあるバージョンを提供していくことを念頭に置いて開発に取り組みました。

その結果

自分がメインのコミッタになったのは 0.0.13 以降ですが、リリースノート を見ると、大きな変更があったらその都度リリースを行っていったので、健全なサイクルになっていったと感じます。

homebrew にもリリースのための Pull Request をしたり、

github.com

プロダクトの命をつなぐためにやれるべきことをしていきました。

まとめ

OSS のメンテナンスは地道な作業の連続です。しかし、 OSS は誰かが面倒を見ないとそのまま放置され、誰にも看取られずプロダクトとしての死を迎えてしまいます。

だからこそ、 OSS のメンテナンスは重要であり、その OSS に利用者がいる限り責任をもって開発に取り組むべきであると思いました。

株式会社ドワンゴでのインターンを終了しました

2017年3月30日付けで株式会社ドワンゴを退職しました。自分はインターンアルバイトとして入社したので、正確にはインターンを終了したということになるのでしょうか。

最初は右も左もわからない状態でしたが、メンターさんたちが根気強く支えてくれたお陰でここまで長い期間取り組めたのだと思います。この場を借りて改めてお礼申し上げます。

さて、せっかくなのでドワンゴでの思い出や取り組んできたことについて綴っていこうと思います。

思い出

2016年8月の入社から8ヶ月、主に Scala と playframework で課金系のシステムを作るお仕事をしていました。最後は新しいプロジェクトの立ち上げにも関わったりしました。

また、Scala 本体や akka とかにコミットしている方が同じフロアにいて席も年齢も近かったので仲良くなれたりとか、 scalaz コミッターのあの方がいらっしゃってランチで交流できたりするのがまた面白いところでした。

労働時間

ドワンゴはフレックス制です。私のチームは完全フレックスだったので、急なスケジュール変更にも対応できすごく助かりました。

大学の講義をこなしながらだったのでこれはとてもありがたかったです。

美容室と整体

ドワンゴには美容室と整体があります。

僕は美容室を何回か利用しました。インターン生でも使えるのでありがたかったのと、勤務時間中に行けるのでコーディングしながら髪を切ってもらえました。

また、表参道の一流の美容師が来てくれるので、結構いい感じに切ってもらえます。

ランチ

銀座はランチがとても美味しいです。色々なところに行きましたが、次のお店がおすすめです。

  • 牛庵
    • ここは最高級のお肉が1000円で食べれるから本当にすごい。最高。

取り組んだこと

入社して最初の頃は管理ツール用の API を作っていました。DDD の D の字も分からなかったのでコードを追うのにとても苦労しましたが、DDD の青い本を買って読んだり、先輩に色々聞いたりしてなんとか身につけました。

あと、インターン生である自分にもレビュアーをやらせていただいたのはとてもいい経験でした。今まで人に見てもらうことしかできなかったので、やってみて人のコードを採点するというのは結構勉強になるなぁと感じました。

その後は、バックログからタスクをもらってこなし、最後はプロジェクトの立ち上げにも関われました。特に、一からバックエンド部分の設計をやれたのはとてもいい経験になりました。

OSS

ドワンゴでは勤務時間中にもある程度は OSS 活動をしてもよいということになっています。なので、業務中に気づいたことがあったら、色々なプロダクトに PR を送ったりしてました。 特に playframework に PR 送ってマージされたときがハイライトでした。

github.com

他には、scalafx という javafxScala ラッパーライブラリの sbt をアップデートしたり、breeze という数値計算ライブラリの build.sbt を整備したりしました。

あとは自分で sbt プラグインを書いたりと、色々やってきたかなーと思います。

github.com

最後に

まだまだやっていきたい気持ちはあるのですが、ラボ生活がはじまるとスケジュールを合わせるのがちょっとむずかしくなるということもあって、キリの良いところで終了となりました。

自分は大学院に進学する予定なので就活はまだ先ですが、そのときは関係者の皆様にまたお世話になるかと思います。よろしくお願いします。

ほしいものリスト

来月から無職(?)なので、よろしくお願いします。

http://amzn.asia/3ArPBpP

東工大ポータルにSeleniumでブラウザから自動ログイン

東工大ポータルにSeleniumで自動ログイン

自分の大学のポータルサイトには二段階認証が付いており、それは一時的にtokenを発行するようなものではなく、よくネットバンキングで使われるようなマトリックス認証である。

セキュリティ的には幾分強くなるのだが、ちょっと使い勝手は悪い(Google二段階認証を使わせてくれないだろうか…)。

そこで、ちょっと不便な現状を打破すべく自動ログインツールを作ろうと思った。

一方、「東工大ポータル 自動ログイン」でぐぐってみると結構な数の「ログインツールつくってみた」系の記事がヒットする。 じゃあこれでは二番煎じなのじゃ…と思ったけど、既存のツールはだいたい

の二種類しかなさそうというのが、ちょっと調べてみて分かった。

そこで今回作ったのはブラウザを操作して自動ログインするツールである。

github.com

追記

より安全な方法に実装を変更し、それにともなって使い方の章を修正しました。

使い方

拙い英語で README.md に記したのだが、大事なことなのでここでもまた説明する。

準備

以下のフローでこのスクリプトを動かす環境ができる。

  • firefox を用意する
  • $ make
  • $ make build.<your os>
    • your os というのは macox, linux64, win64 である。

ログイン(追記)

以前の方法ではリポジトリに直にパスワードを置くためセキュリティに問題があった。 ここで、pitというライブラリを使い、設定ファイルを外に置くことにした。 設定の方法は、make したあとに

$ vi ~/.pit/default.yaml
titech:
  usr_name: YOUR_USER_NAME
  usr_password: YOUR_PASSWORD
  usr_matrix: ['xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx', 'xxxxxxx']

default.yaml を編集すればOK。 usr_matrix はAからJまでの列にあるアルファベットを配列でflfejfewみたいに記入する。

ログインする

  • $ make login

これでOK。

実装

Selenium-webdriverを使って簡単なスクリプトRuby で書いてみた。簡単に説明していく。

@driver = Selenium::WebDriver.for :firefox

これでブラウザを操作するドライバーインスタンスを作っている。これがSeleniumスクレイピングするときのすべての始まり。

以下は、細々とした実装になってしまった。

@driver.get 'https://portal.nap.gsic.titech.ac.jp/GetAccess/Login?Template=userpass_key&AUTHMETHOD=UserPassword'
usr_name = @driver.find_element(:name, 'usr_name')
usr_name.send_keys(@usr_name)
usr_pass = @driver.find_element(:name, 'usr_password')
usr_pass.send_keys(@usr_password)
@driver.find_element(:name, 'OK').click

sleep 3

ここで Basic 認証を突破している。

@driver.find_element(:name, 'usr_name') でIDを入力するelementを見つけてきて、そこにusr_nameを入力。

passwordの部分も同じである。

sleep 3 は認証してレスポンスが返ってくるまで時間がかかってしまい、waitしないとエラーになるため入れいている。

def get_password_value_from_message(message)
  @usr_matrix[message[0]][message[1] - 1]
end

def get_message_value_from_xpath(element)
  [element.text[1], element.text[3].to_i]
end

xpath_list = [
  '/html/body/center[3]/form/table/tbody/tr/td/table/tbody/tr[5]/th[1]',
  '/html/body/center[3]/form/table/tbody/tr/td/table/tbody/tr[6]/th[1]',
  '/html/body/center[3]/form/table/tbody/tr/td/table/tbody/tr[7]/th[1]'
]

pass_list = [
  @driver.find_element(:name, 'message3'),
  @driver.find_element(:name, 'message4'),
  @driver.find_element(:name, 'message5')
]

message_value = xpath_list.map { |e|
  get_message_value_from_xpath(@driver.find_element(:xpath, e))
}

pass_list.zip(message_value).map { |pass, message|
  pass.send_keys(get_password_value_from_message(message))
}

@driver.find_element(:name, 'OK').click

これは、xpathマトリックス認証に必要な要素を見つけて来て、そこから必要なアルファベットを取得している。 この部分が一番実装していて面倒だった。

最後に

東工大ポータルのログインツールについて色々調べてみたので、あとでこれらについてまとめて記事にしてみても面白そうな気がした。

sbt plugin の作り方 - 実際の plugin を用いて解説 -

概要

sbt plugin の作り方をこちら

github.com

のコードを使いながらざっくり解説していきます。

sbt plugin を作る

sbt plugin はいくつかの Key に対してその振る舞いを記述していく、というスタイルで実装していきます。 振る舞いとは、例えば

  • Groovy など ScalaJava 以外のソースをコンパイルする
  • プロジェクトを jar に固めてデプロイする

などがあると思います。

sbt plugin はおおまかに分けて

  • Key
  • Task
  • projectSettings

という三つの要素があり、plugin 開発者はそれぞれを実装していく必要があります。

そのテンプレートは次のようになります。

import sbt._
import Keys._

object SimplePlugin extends AutoPlugin {

  object autoImport {
    /** implement your keys */
    ???
  }

  import autoImport._

  override def trigger: PluginTrigger = allRequirements

  override val projectSettings = ???

  lazy val yourTask = Def.task { 
    /** Implement your task(s) */
    ??? 
  }
}

Key

Key には以下の三種類があります。

  • settingKey: 属性、可変の値
  • taskKey: 実際の振る舞い
  • inputKey: taskKey でコマンドライン引数を受け取りたい場合の振る舞い

今回は settingKeytaskKey を使いました。

実際に定義したいのは次の4つです。

  • jflex ファイル *.flex の場所
  • 生成するファイル *.scala の場所
  • 実際に生成する振る舞い
  • その他設定

しがたって、以下のように実装しました。

 object autoImport {
    lazy val jflexSourceDirectory = settingKey[File]("jflex-source-directory")
    lazy val jflexOutputDirectory = settingKey[File]("jflex-output-directory")
    lazy val toolConfiguration = settingKey[JFlexToolConfiguration]("jflex-tool-configuration")
    lazy val pluginConfiguration = settingKey[PluginConfiguration]("jflex-plugin-configuration")
    lazy val jflexGenerateWithCompille = settingKey[Boolean]("jflex-with-compile")
    lazy val jflexSources = taskKey[Seq[File]]("jflex-sources")
    lazy val jflexGenerate = taskKey[Unit]("jflex-generate")
  }

Task

Key を定義したら次は Task を定義します。Task とは「実際の動作」であり、jflex-scala-plugin では

  • jflex API を叩いて *.flex ファイルを生成する

という振る舞いが想定されます。したがって、次のように実装しました。

lazy val jflexGeneratorTask: Def.Initialize[Task[Unit]] = Def.task {
    generateWithJFlex(
      jflexSources.value,
      jflexOutputDirectory.value,
      toolConfiguration.value,
      pluginConfiguration.value,
      streams.value.log
    )
  }

  private[this] def generateWithJFlex(
    srcDir: Seq[File],
    target: File,
    tool: JFlexToolConfiguration,
    options: PluginConfiguration,
    log: Logger
  ): Unit = {
    target.mkdirs()

    log.info(s"JFlex: Using JFlex version ${Main.version} to generate source files.")
    Options.dot = tool.dot
    Options.verbose = tool.verbose
    Options.dump = tool.dump
    Options.setDir(target.getPath)
    Options.emitScala = tool.emitScala

    val grammars = (srcDir ** ("*" + options.grammarSuffix)).get
    log.info(s"JFlex: Generating source files for ${grammars.size} grammars.")

    grammars.foreach { g =>
      Main.generate(g)
      log.info(s"JFlex: Grammar file ${g.getPath} detected.")
    }
  }

projectSettings

ここには、デフォルトの挙動(初期値)を記述していきます。

sbt-jflex-scala の場合、次のような初期値を設定しました。

  • *.flex ファイルはデフォルトで src/main/flex にする
  • 生成される *.scala ファイルはデフォルトで src/main/scala/flex にする
  • コンパイル時の自動生成はデフォルトではオフ

したがって、次のような実装になりました。

  override val projectSettings: Seq[Setting[_]] = Seq(
    jflexSourceDirectory := sourceDirectory.value / "main" / "flex",
    jflexOutputDirectory := sourceDirectory.value / "main" / "scala" / "flex",
    toolConfiguration := JFlexToolConfiguration(),
    pluginConfiguration := PluginConfiguration(),
    jflexSources := (jflexSourceDirectory.value ** "*.flex").get,
    jflexGenerate := jflexGeneratorTask.value,
    unmanagedSourceDirectories in Compile += jflexSourceDirectory.value,
    jflexGenerateWithCompile := false
  ) ++ Seq(
    compile := {
      if (jflexGenerateWithCompile.value)
        (compile in Compile).dependsOn(jflexGenerate).value
      else
        (compile in Compile).value
      }
  )

これで、sbt plugin のパーツが整ったので、あとはテンプレート部分を埋めるだけです。

全体像はこちらを参照してください。

参考

jflex-scala の sbt plugin を作った話

JFlex とは

JFlex とは Java で書かれた構文解析器を生成するためのライブラリで、Javaのコードを生成します。

sbt-jflex-scala を作った動機

JFlex の fork の中で jflex-scala というものがあり、これは Scala のコードを生成してくれます。

github.com

これを sbt 上で利用するための sbt プラグインはいくつかあるのですが、どれもあまりメンテされておらず、動作しないため、この際新しく作り直しました。

3tty0n/sbt-jflex-scalaホスティングしてあります。

github.com

使い方

使い方は単純で

  • project/plugins.sbt に次の記述を追加
 addSbtPlugin("com.github.3tty0n" % "sbt-jflex-scala" % "0.1.1")
  • *.flex ファイルを src/main/flex に置く
  • $ sbt jflexGenerate

Yylex.scala が生成されます。

オプションで

jflexWithComple := true

とすると、コンパイル時に Yylex.scala を自動生成してくれます。

最後に

イマイチ需要はわからないけど、構文解析する人にとってはちょっと便利になるかもしれません。

この次は sbt plugin の作り方をまとめてみたり、 Elm の話もできたら良いと思っています。