09

職人作業を Jenkinsfile にした

もう新しくもない話になるが、 Jenkins 2 になってから、Jenkinsfile というDSLで記載したファイルにビルドパイプラインの定義をスクリプトとして記述することができるようになっている。 テキストで書けるようになることでバージョン管理による差分の検出・追跡が可能になる。Infrastructure as Code のトレンドがあった影響だとは思うが、まあJenkins自体が重量級なのでどうなのか。と思うところはあるが、ないよりは断然良い。

自分の所属先ではパッケージ型のプロダクトを持っており、以前よりそのビルドやリリースのためにJenkinsは利用している。新しいリリースを計画するたびに以前のジョブをいくつかコピーしながら新しいリリース向けのビルドジョブを作成し、それらをつなげてリリースパイプラインにしていた。 これらのインフラは作ったのも自分なので、何となく毎回なにが必要なのかはわかってはいるのだが、いまいち定型化できないし説明も難しい感じがしていた。とりあえずリリースにあたってやる必要のあることを整理する目的で、今回の一連のビルドの一部(特にリリースに関する部分)をJenkinsfileにした。

Jenkinsfile自体2通りの書き方があるらしい。Declarative PipelineとScripted Pipelineといって前者はあくまで宣言的に記述していくのに対して後者はGroovyスクリプトを混ぜ込めるというか、Groovyとして評価されるっぽい?一応前者は pipeline の宣言がルートにあり、後者はそうではないということになっているらしいのだが、この区別はほんとにそうなのかよくわからない。というか、特に興味がないのであまりこれらの違いに関して理解していない。 というのも、Declarative Pipelineでは柔軟なビルドパイプラインの定義が難しいと感じたため、結構Groovyスクリプトを書いてしまっている。例えばDeclarativeでは繰り返しの定義や、データに従ってコンフィグが変化するビルドを定義することができない、もしくは難しい。自分の要求としては、成果物全体の姿は決まっているが、例えばパッチリリースだった場合は成果物の一部、必要な部分のみに限ってリリース対象となるため、リリース毎にリリース対象を決め、その定義に従って適当に必要な部分だけパイプラインが流れるようになってくれないと面白くない。 リリース対象の増減でパイプラインの定義を書き換える必要があるのであれば、事故のもとにもなるだろうしSCMが使えるとはいえメリットが薄れる。

これをループなしで実現するのはちょっと難しいと思われた。また確認はしていないのだが、おそらく処理の共通化が容易ではないように思われる。可能な方法としては以下のような手段が取れそうなのだが

  • ジョブを分けて、共通的なジョブは個別のジョブの下流に置く
  • Groovyスクリプトを切り出して、動的に読み込む処理を書く

ただ前者の場合、成果物がジョブのワークスペースにできると思うので、いろいろなバージョンの成果物がひとところに集まるのであれば微妙。後者の場合・・・というか、結局Groovyから逃げるなということではないのか。 そう理解をして、結局今回取っている方針は、

  • 1ファイルにリリースパイプラインのほぼすべてを記述する(単純な個別ビルドは別途定義してあって基本としてはそこから成果物を収集する)
  • 可変部分はヘッダに定数的なオブジェクトを作ってそこに定義できるようにする。
  • 次回リリース時には、ファイルはまるまるコピーしてヘッダ部分のみリリースに必要な内容に書き換えてパイプラインを作成する。

処理の共通化はしていない。してもよいのだが、共通化したファイルがどこにあれば適切なのかいまいち判断できなかったことと、過去のリリースは現在のリリースと関係ないし、共通化した処理を修正したら過去のビルドも治ってほしいということもないように思われるので毎回コピーでも許容できそうに思われたため。

JenkinsfileはSCMに放り込むのだが結局Jenkinsのビルドジョブは作る必要があるあたり微妙なのだが。Gitを使えばもうちょっと自動的にやれそうな雰囲気を感じてはいる。今のところSVNなのであまり確認できていない。 (まあでもJenkinsファイルのリポジトリだけGitにしてしまえば試せるかもしれない。)

というわけで、職人作業はほぼGroovyスクリプトになった。実際にうまく運用できるかは次回以降のリリースでも確認する必要がある。

で今のところ、 Declarative Pipeline のメリットを見いだせていない。

Arduino で 複数のサーボモータを同時に動かす

何って、単にこういうこと


20180512

Arduino本体にデジタルピン経由でボタンを二つ、I2CでPCA9685なる複数のサーボモータを制御するボードを介して3基のサーボモータを制御します。別に難しくもなんともないんですが、ソースコードをどこかに張り付けておきたいと思ったのでこんなエントリを書いている次第。

Keyestudio

最近中国は深センあたりのメーカーが元気いいらしいじゃないですか。まあ行ったことないんでよく知らないんですけど。まあArduinoはオープンなハードウェアなわけで、深センのメーカー何社からArduinoクローンが発売されています。叩き合っているのか何なのかわかりませんが、本体とそこらの入門本で使いそうなパーツをいろいろ取り揃えてパックにしたものスターターキットとして大変お安く販売されています。品質も詳しくないのでまあ高いといえるのかどうかわかりませんが、日曜工作くらいなら普通に買って不安がないくらいではあります。

で、動画に見える通り今回は Keyestudio ってとこのを使っています。黄色がかっこよかったので。しかも特に意味もなく環境モニタリングキットという、細かい電子部品の代わりにいろんなセンサーをまとめてパックにされたものを買っています。いずれ何かに使うでしょう。

(なぜかはてなのアマゾン商品紹介機能が使えないので直接アフィリンク張っておきます)

PCA9685

Arduinoから複数のサーボモータを使う場合にこういうのを使う必要がある(?)らしいです。特にArduino単体でやってみてできなかったので買った、というわけではなく、ネットで検索してたら出てきたので買ってみたというだけなのであれですが。

特に説明書も何もついてないんでどうするんだという感じなんですが、Amazonの商品ページに行くと接続例の回路図が商品画像の一部に載ってます。つまりその通り接続します。物理はそれでいいとしてソフトウェアのほうはどうするかというと、これもAmazonの商品ページ見るとわかるんですが、「Adafruit 16-Channel Servo Driverの互換品」と書かれています。なので Adarfuit のライブラリがそのまま使えます。Arduino IDEのスケッチメニューのライブラリをインクルード>ライブラリの管理 から Adafruit PWM で 検索すれば出てきます。あとはこのライブラリの使い方を適当にググりながら書けばよいという感じです。

最初付属の説明が何もなくてどうすればいいのかという状態だったので、いろいろ試行錯誤しましたが無駄でした。なので今後そんな人類が生まれてはいけないと思いここに書き記しておきます。

ソース

たいして難しくもないのですがサーボは可動可能範囲があるので、その範囲をどのくらいの値として指定する必要があるのかは製品によって違うらしいですね。なので手探りで割り出す必要がありそう。ちなみにこのソースに書いてあるのはほかの人が今回私が使用しているサーボモータと同じSG90で試したと思われる値をパクっています。

ArduinoでPCA9685を使って複数のサーボモータを回してみる

Java EE でPlugin的な拡張ってどんな扱いなの

あるウェブアプリケーションを提供する場合にプラグイン的な機構を設けて、利用するユーザごとに機能拡張であるとかカスタマイズさせたい場合、なんていうユースケースは昨今いろいろありそうではあるけど、Java EE的にそういった機構を実現するための仕様ってあるんだろうか。 基本的にウェブアプリケーションはWarという形でまとめられてしまうからそこ以外からの干渉はできない気がする(むしろ独立した環境がパッケージングされているということに意味がある感じがする) 例えば単純にあるインタフェースを持ったクラスを運用中に追加して、アプリケーションの動作を変更するなり拡張するなりができる、とした場合、追加のClasspathというか追加のクラスローダが必要になる気がするのだけどそれはアプリケーションが独自にやればいいという話でJavaEEでは仕様化されていないのかな。

ただ全体としてはJavaのコードだけではダメでクライアントサイドスクリプトとかイメージのようなスタティックファイルなんかも動的に追加したいとなった場合、どうなんだろう。それ専用のコンテンツローダを作るのだろうか。

アプリケーションサーバによってはサーバ側の設定でやれそうではあるけど。

com.caucho.java.CompileClassNotFound: illegal utf8 encoding at (140)

e-Builder 7.2.x でデバッグサーバを起動しようとして以下のような例外で止まる。 昨日までは動いてたのにな?

警告: com.caucho.java.CompileClassNotFound: illegal utf8 encoding at (140)
com.caucho.java.CompileClassNotFound: illegal utf8 encoding at (140)
    at com.caucho.loader.CompilingLoader.compileBatch(CompilingLoader.java:749)
    at com.caucho.loader.CompilingLoader.make(CompilingLoader.java:353)
    at com.caucho.make.MakeContainer.make(MakeContainer.java:70)
    at com.caucho.loader.DynamicClassLoader.make(DynamicClassLoader.java:1134)
    at com.caucho.loader.EnvironmentClassLoader.scan(EnvironmentClassLoader.java:526)
    at com.caucho.loader.DynamicClassLoader.sendAddLoaderEvent(DynamicClassLoader.java:746)
    at com.caucho.loader.DynamicClassLoader.init(DynamicClassLoader.java:1163)
    at com.caucho.loader.EnvironmentClassLoader.init(EnvironmentClassLoader.java:186)
    at com.caucho.loader.ClassLoaderConfig.init(ClassLoaderConfig.java:156)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.caucho.config.j2ee.PostConstructProgram.inject(PostConstructProgram.java:60)
    at com.caucho.config.type.BeanType.init(BeanType.java:226)
    at com.caucho.config.ConfigContext.configureChildNode(ConfigContext.java:522)
    at com.caucho.config.ConfigContext.configureAttribute(ConfigContext.java:327)
    at com.caucho.config.program.NodeBuilderChildProgram.inject(NodeBuilderChildProgram.java:56)
    at com.caucho.config.program.ContainerProgram.inject(ContainerProgram.java:80)
    at com.caucho.config.program.ConfigProgram.configure(ConfigProgram.java:61)
    at com.caucho.server.deploy.EnvironmentDeployController.configureInstance(EnvironmentDeployController.java:364)
    at com.caucho.server.deploy.EnvironmentDeployController.configureInstance(EnvironmentDeployController.java:55)
    at com.caucho.server.deploy.DeployController.startImpl(DeployController.java:665)
    at com.caucho.server.deploy.StartAutoRedeployManualStrategy.startOnInit(StartAutoRedeployManualStrategy.java:74)
    at com.caucho.server.deploy.DeployController.startOnInit(DeployController.java:549)
    at com.caucho.server.deploy.DeployContainer.start(DeployContainer.java:160)
    at com.caucho.server.webapp.WebAppContainer.start(WebAppContainer.java:659)
    at com.caucho.server.host.Host.start(Host.java:450)
    at com.caucho.server.deploy.DeployController.startImpl(DeployController.java:667)
    at com.caucho.server.deploy.StartAutoRedeployAutoStrategy.startOnInit(StartAutoRedeployAutoStrategy.java:72)
    at com.caucho.server.deploy.DeployController.startOnInit(DeployController.java:549)
    at com.caucho.server.deploy.DeployContainer.start(DeployContainer.java:160)
    at com.caucho.server.host.HostContainer.start(HostContainer.java:484)
    at com.caucho.server.cluster.Server.start(Server.java:1319)
    at com.caucho.server.cluster.Cluster.startServer(Cluster.java:710)
    at com.caucho.server.cluster.ClusterServer.startServer(ClusterServer.java:542)
    at com.caucho.server.resin.Resin.start(Resin.java:703)
    at com.caucho.server.resin.Resin.initMain(Resin.java:1162)
    at com.caucho.server.resin.Resin.main(Resin.java:1365)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at jp.co.intra_mart.bin.server.core.WebApplicationServer.run(WebApplicationServer.java:259)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.CharConversionException: illegal utf8 encoding at (140)
    at com.caucho.vfs.i18n.UTF8Reader.read(UTF8Reader.java:158)
    at com.caucho.vfs.i18n.UTF8Reader.read(UTF8Reader.java:184)
    at com.caucho.vfs.AbstractByteToChar.readChar(AbstractByteToChar.java:153)
    at com.caucho.vfs.AbstractByteToChar.addChar(AbstractByteToChar.java:125)
    at com.caucho.java.JavacErrorParser.parseErrors(JavacErrorParser.java:74)
    at com.caucho.java.ExternalCompiler.compileInt(ExternalCompiler.java:218)
    at com.caucho.java.AbstractJavaCompiler.run(AbstractJavaCompiler.java:102)
    ... 1 more 

デバッグサーバを再構築したりワークスペースを切り替えたりしたけどダメで結局e Builder再インストールしたら直った。

clojure 書いてみた日記

intra-mart Web Platform 7.2.x では以下のようなプレゼンテーションページを書いた場合

<html>
  <body>
     <!-- <imart type="string" value="hoge" ></imart>--> 
  </body>
</html>

コメントの中のimartタグは評価されず、そのまま出力される。 つまり、クライアント側に <imart type="string" value="hoge" ></imart> が(ソース表示すると)そのまま見える。 これが intra-mart Accel Platform になると挙動が変わっており、コメントの中のものも評価されるようになっている。 つまり上記を intra-mart Accel Platform で実行した場合クライアントに対して出力されるのは

<html>
  <body>
     <!-- hoge -->
  </body>
</html>

というHTMLになる。この例だと多分最終的な見た目は変わらないからいいんだけど、条件分岐を評価させないようにというつもりでコメントアウトしていると厄介なことになる。

<html>
  <body>
     <!-- <imart type="condition" validity=oValue >--> 
     必ず出力したい文字
     <!-- </imart> -->
  </body>
</html>

intra-mart Web Platform 7.2.x では必ず出力したい文字は出力されるが、 intra-mart Accel Platform ではoValueの値によっては出力されなくなる。

これがintra-mart Web Platform 7.2.x から Accel Platform へ移行する際に困った問題の一つ。

一応正規表現を使って一括である程度対応できるものの、完全にチェックするのはなかなか難しい。 正規表現仙人であれば可能なのかもしれないが、個人的にはその域に達していないので、 どうにもこれが残ってしまっていないか不安だった。

一応実案件ではテストもするので、必要な対応はできていたと思っているけど、まあ簡単にスクリプトでも書けば 精度も効率も上がるだろうなと思っていて、一つスクリプトを書いてみることにした。 言語は長いことかけてのんびり勉強している Clojure。

要件

やりたいことを予め上げておくと

  • HTMLコメントに隠された imart タグを探す。
  • 多分問題になりうる文字列は<imartとか</imart>とか。
  • 特定のフォルダ配下を一括してチェックしてファイルパスと発見した行数と内容を出力したい。
    • 勝手に問題が解消するよう編集してくれてもいいけど、怖いのでそこまではしないでおこう

成果物

まあどの程度正確にやれてるかあやしいんだけど一応以下に上げてみた https://github.com/carrotsword/commented-imart-detection

ご査収ください。

タグの文字列をある程度固定で探しているんだけど、空白なんかを正規表現で あいまいにマッチさせた方がいいのかもしれない。

Clojureについて

Clojure の勉強用に書いてみたものなので、至らない点は多々(テストは書いているけどまだ実戦に投入はしていないので)あると思うけど Clojureを書きながら普段Java/Javascriptプログラマである自分がどんなことを考えたかということを残しておきたい。

不変性(immutability) と 関数指向

基本的にはデータをどういう集合としてとらえて、ある集合から別な集合への変換をどう考えるか、 ということを考えるのに終始していた気がする。

普通にJavaでプログラミングする場合には状態の遷移はなるべく状態のライフサイクルと マッチするようなオブジェクトを(クラスとして)設計して、状態の管理はそのオブジェクトの 中に閉じ込めて考える、という感じに設計していくだろう。

(多分関数型全般の)特性として、基本的にそういう考え方ができない(あるいは推奨されない)。 データ=状態で、状態を直接変更することは許されない(わけじゃないんだけど)から、 状態を変更するという操作はある状態Aをもとに別の状態Bを生成する形になるんだろう。

その状態遷移を記述するのが関数ということになる。

なので、基本的に状態遷移の時系列がコード上は関数の羅列という形で記述される。

オブジェクト指向の場合は状態の遷移に関する操作が一か所に集まるような 設計になる(もちろんならない人もいるが)のが普通だろうが、これは必ずしも 状態の遷移を明確に記述することにはならない。 (状態の遷移の時系列は一般にオブジェクトの外側からどう呼ばれるか次第なので、 そのオブジェクトの中にはない場合が多い)

状態Aから状態Bに遷移させる関数の適用を A => B と表現するならば、コード上の記述は

A => B
B => C
C => D

といった記述になるだろう。Clojureは動的型付け言語だけど、静的型付な環境であれば、 上記のA~Dはそれぞれ異なる型となるのだろうか。 そうであれば、型さえわかればできることは自明になるのかもしれない。

ただまあ、とにかくすべてを上記のように割り切って考えられるというわけでもない。

標準ライブラリ

とにかく googlability が低いという印象。 Clojureのコードを書いているときはMacならDash、 Windowsならブラウザで公式サイトを開きっぱなしにして調べつつコードを書いていた。

まあ、悪くないんだけど、今まさに学習中なのに、自分が知らない概念にたどり着くのが難しい感じがする。 Javaだったらこう書くんだけど、をGoogle先生に聞いて、結局自分の中のJavaコードをClojureにマッピングしているだけなのではという感じがあるのだけど、たまに聞いたこともないClojure用語的なものが出てきて、適当に検索してみてもなんのことか全くわからないということがある。

自分の知らない概念がまだまだあるんだろうなあ、という漠然としたイメージだけはある。まあドキュメント上からちゃんと読めということなのかもしれない。

S式

あるデータhogeに対してAしてBしてCしたいという場合にはまあこうなる

(C (B (A hoge)))

スレッドマクロというのがあって、こう書ける

( ->> hoge
  (A)
  (B)
  (C))

分かりやすい! が、それでいいんだっけ?という気持ちになる。 結局S式わかりにくい。

REPLとかコーディングのワークフローとか

REPLで小さな関数をどんどん試しながらボトムアップにコードを書いていくのがいいらしいのだけど、その辺の感覚がまだわからない。 頭の中のコードをとりあえずガーッと書き出してしまう感じ。なので実際動かしてみるといろいろ思っていたのと違う結果になってしんどい。

デバッグの方法もまだあまりしっくりきていない。以下の記事は大いに参考にさせてもらったのだけどEmacs使いでもないのでな・・・。

ayato.hateblo.jp

遅延シーケンス

頭ではわかっていても、実際経験してみないとわからないというか、気を付けられないものだなというのを改めて知った感じ。

Java VM の読み込み中にWindowsのエラー2が発生しました

今更ではあるけど、最近のPCの環境にintra-mart e-Builder 7.2.x をインストールしようとすると、タイトルのエラーが表示されてインストールできない。 どうもこれと類似の問題のようだ

www-01.ibm.com

インストーラがInstallAnywareを使って作られていて、それがもうだいぶ古いのか、最新のJava8の入っている環境ではうまく動かないようだ。

LAX_VM オプションで使用するVMを指定することができる。

C:\> eBuilder726.exe LAX_VM "C:\Program Files (x86)\Java\jdk1.7.0_55\bin\java.exe"

ちなみにここで必要だったか正確に覚えていないが、どこかの時点で32bitVMがないとやはり途中で失敗したはず。

Windows 10だとさらにオプションが必要だった

C:\> eBuilder726.exe LAX_VM "C:\Program Files (x86)\Java\jdk1.7.0_55\bin\java.exe" -i GUI

なんでだかよくわからないけど。

そなえよう

ロジクール G810 RGB Romer-G メカニカル ゲーミング キーボード

アフィリエイト張ってみたかったのでやってみるコーナーです。

プログラマなんていう職業をやっていると気分転換にキーボード変えたりマウス変えたりするわけですが、少し前に職場用にこれを買った。

Logicool ロジクール G810 RGB Romer-G メカニカル ゲーミング キーボード

Logicool ロジクール G810 RGB Romer-G メカニカル ゲーミング キーボード

なかなかお気に入り。以下具体的に気に入っている点を挙げると:

  • メカニカルという割に静か
    • 押した感じ「カチャカチャ」というよりは「さく、さく」という感じ
    • 今までCORSAIRの赤軸(後述)を使ってたんだけど、結構キーの音が大きいというか、多分底突きの音が本体に反響していて、これが結構気になる。
    • 聞き比べてもはっきりと静かになった。
  • キーが軽い
    • 多分赤軸も大分軽いのだと思いますが、それと比べても若干軽い。
    • ただRomer-Gはなんか押し下げ感にざらつきがある気がする。これはもしかするといやに感じる人がいるかもしれない。
  • 重い
    • キーボード重量がこのサイズにしてはだいぶずっしり来る感じ。まあこの価格帯のキーボードならびっくりするほどではないけど。
  • キートップがレインボーカラーに光る
    • Romer-Gの売りの一つ
    • まあ別に・・・

前まで使ってたキーボードというのはこれなんですが

CORSAIR ゲーミング キーボード K65 Compact JP

CORSAIR ゲーミング キーボード K65 Compact JP

散らかし放題なデスクトップで戦っていく必要があり、コンパクトでよかったのですが、打鍵音がカーンと響く感じで耳に残る。

ちなみにRomer-GでいうとG910というキーボードが去年、Romer-Gの初搭載機ということで発売になっていました。

LOGICOOL RGB メカニカル ゲーミング キーボード G910

LOGICOOL RGB メカニカル ゲーミング キーボード G910

これが発売された時からRomer-Gには興味があったものの、さすがにこのデザインでは辛いため見送っていたのですが、今年になってすっきしりしたのが出てきたので発売のニュースを見つけた瞬間に買ったという感じです。

ちなみにキーボードもマウスも有線派です。突然つながらなくなった時に原因が電池なのか、無線系統の不調なのかよくわからんという状態になったことはありませんか。電池を変えたりデバイスのペアリングを削除して再登録してみたり、デバイスドライバを更新したりしてなんとか復旧したものの結局電池ではなかったのか?みたいなイライラに耐えかねて無線は買わないことにしました。

ゲーミングキーボードとかゲーミングマウスばっかり買ってるんだけど、別にレイテンシが~とかキー同時20っこ押し!とかするわけではなく、単純に基本的な機能の信頼性が高いのではと思って買っている感じ。