Doma 2をGroovyで使用する時に気を付けること
気付いた範囲でまとめてみます。
フォルダ配置
Doma 2はAnnotation ProcessorでDAOの実装クラスを自動生成します。Doma 2のAnnotation ProcessorはJavaのコードにのみ対応しているため、DAOやエンティティはGroovyではなくJavaで書く必要があります。また、GroovyとJavaのコードが互いに参照する場合は両者を /src/main/groovy
に配置する必要があります。
具体的には以下のような配置になります。
src/main/
(4/18追記)エンティティクラスもAnnotation Processorで処理されるため、Javaで書く必要があるようです。
ビルドの設定
GroovyとJavaを /src/main/groovy
に配置した場合、GroovyコンパイラがAnnotation Processorを実行することになるため、以下のように compileGroovy
タスクに対して設定を適用します。
// build.gradle plugins { id 'groovy' } dependencies { compile 'org.seasar.doma:doma:2.16.0' } processResources.destinationDir = compileGroovy.destinationDir compileGroovy.dependsOn processResources
外部ドメインを利用する場合などAnnotation Processorの設定が必要な場合は、Groovyコンパイラに設定を渡します。
compileGroovy.options.compilerArgs = ['-Pdoma.domain.converters=example.framework.data.HelloConverter']
2.16.0からはAnnotation Processorの設定を /src/main/resources/doma.compile.config
に書けます。これにより、ビルドツールやIDEでAnnotation Processorを設定する必要がなくなります。
その他
ドメインクラスは @Immutable
や @Canonical
を使うと扱いやすいです。
Create React AppでChrome Extensionを開発する
Reactのアプリを開発するにはFacebook謹製のcreate-react-appが便利です。しかし、create-react-appはWebアプリの開発に特化しているため、Chrome Extensionの開発には使えない問題があります。スクリプトを少し変えてwatch buildを行う方法を紹介します。
Chrome Extensionの開発に必要なフロー
Chrome Extensionを開発するには以下のようなフローが必要になります。
- アプリをビルドし、
build
フォルダに出力する。 - Chromeの拡張機能を開き、デベロッパーモードで
build
フォルダを読み込む。 - Chrome Extensionを実行する。
- ソースコードを修正する。
- アプリをビルドし、
build
フォルダに出力する。 - 3に戻る。
create-react-appでnpm startを実行するとlocalhost:3000
で開発サーバが実行されます。localhost:3000
をそのまま開いても通常のWebアプリとして扱われるため、Bookmarks APIなどのChrome APIを実行できません。Chrome APIを実行するには上記の2が必要になります。
スクリプトのカスタマイズ
create-react-appは内部でWebpackの開発サーバを実行しています。これをwatchに変更します。
今回は以下の方針で修正します。
- ejectして修正するとバージョンアップに追随するのが辛いのでやりたくない。
- create-react-appが持っているWebpackの設定(
webpack.config.dev.js
)をなるべくそのまま利用したい。
まず、package.jsonを以下のように修正します。
"scripts": { "start": "node scripts/start.js", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "zip": "zip -j - build/* > build/extension.zip", "clean": "rm -fr build" }
scripts/start.jsを作成します。
process.env.NODE_ENV = 'development'; var fs = require('fs-extra'); var paths = require('react-scripts/config/paths'); var webpack = require('webpack'); var config = require('react-scripts/config/webpack.config.dev.js'); // removes react-dev-utils/webpackHotDevClient.js at first in the array config.entry.shift(); var compiler = webpack(config); compiler.watch({}, function(err, stats) { if (err) { console.error(err); } else { copyPublicFolder(); } console.error(stats.toString({ chunks: false, colors: true })); }); // as react-scripts/scripts/build.js function copyPublicFolder() { fs.copySync(paths.appPublic, paths.appBuild, { dereference: true, filter: file => file !== paths.appHtml }); }
ちょっと雑ですが、create-react-appが持っているWebpackの設定を一部改変してwatchに渡すことで継続的にビルドを実行できます。
これでChrome Extensionの開発に必要な下表のタスクが実行できるようになりました。
コマンド | やること |
---|---|
npm start |
ファイルが変更される度にdevelopmentでビルド |
npm build |
productionでビルド |
npm zip |
ビルドをZIPファイルに固める |
ご参考まで。
Jenkinsfileによるジョブ管理のメリットと実例
ジョブの設定をJenkinsfileで管理し始めてから3か月ぐらい経ったので、知見をまとめてみます。
Jenkinsfileを使うメリット
Jenkinsの画面でジョブを管理していると以下のような問題が起きることが多いと思います。
- 誰かが勝手にJenkinsの設定を変更して動かなくなった
- ジョブ設定を別リポジトリに横展開したいけど、ポチポチ設定するのが面倒
JenkinsfileをGitで管理することで、以下のメリットがあります。
- いつ、誰が、なぜジョブ設定を変更したのか後から調べられる
- Pull Requestでジョブ設定の変更をレビューできる
- ブランチを使ってジョブ設定を試行錯誤しやすい
Jenkinsの運用ポリシー
前項のメリットを実現するには、Jenkinsを以下のポリシーで運用することが望ましいでしょう。
- Jenkinsの設定は最小限に抑える
- なるべく画面からジョブ設定を変更せずに済むようにする(GitHub OrganizationやMultibranch Pipelineを利用)
- なるべくJenkinsにログインしなくてもオペレーションが回るようにする(ビルド結果をチャットに通知)
- Jenkinsfileをポータブルにする(認証情報を書かない、Jenkins Agentをイミュータブルにする等)
Jenkinsfileのテンプレート
どの言語でも共通のテンプレートを用意しておくと便利です。以下のようなテンプレートを利用しています。
node { try { checkout scm stage('build') { try { // TODO: ビルドを実行する // TODO: チャットにビルド成功を通知する } finally { // TODO: テスト結果を収集する (junit, publishHTMLなど) } } } catch (e) { // TODO: チャットにビルドエラーを通知する throw e } }
ビルド結果はチャットの専用チャンネルに通知するようにしています。ただし、masterブランチのビルドエラーが起きた場合はすぐに直す必要があるので普段のチャンネルに流すようにしています。
} catch (e) { // TODO: チャットにビルドエラーを通知する if (env.BRANCH_NAME == 'master') { // TODO: チャットにmasterが壊れたと通知する } throw e }
GradleでJVM言語をビルドする
GradleでJava、Groovy、Kotlinなどをビルドする場合は以下のようになります。
node { try { checkout scm sh 'chmod +x gradlew' stage('check') { try { sh './gradlew check' } finally { // TODO: テスト結果を収集する junit allowEmptyResults: true, testResults: 'build/test-results/test/*.xml' publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: false, reportDir: 'build/reports/jacoco/test/html', reportFiles: 'index.html', reportName: 'Coverage']) publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: false, reportDir: 'build/reports/tests/test/html', reportFiles: 'index.html', reportName: 'Test']) } } // TODO: チャットにビルド成功を通知する } catch (e) { // TODO: チャットにビルドエラーを通知する throw e } }
ビルド実行時の情報を使う
ビルド実行時に取得できる情報は、JenkinsのGlobal Variable Referenceで確認できます。例えば、 env.BRANCH_NAME
でブランチ名を取得できるので、masterブランチの場合にのみ特殊な処理を行うといったことも簡単にできます。
if (env.BRANCH_NAME == 'master') { // masterブランチの場合はSonarQubeの静的解析を行う sh './gradlew sonarqube' }
また、パスワードやアクセストークンのような認証情報はJenkinsで管理し、ビルド実行時に注入させることができます。
withCredentials([usernamePassword(credentialsId: 'xxx', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { // S3にリリースする }
まとめ
本稿ではJenkinsfileでジョブ設定を管理するメリットを説明し、Jenkinsfileの実例を紹介しました。