Kengo's blog

Technical articles about original projects, JVM, Static Analysis and JavaScript.

外国人永久居留証を取得しました

外国人永久居留証の申請条件を満たしたので、2020年9月25日付で外国人永久居留証を申請し、半年後の2021年2月25日に受け取りました。

どういうカードなのか

俗に言う中国版グリーンカードですが、だいたい以下の恩恵があるようです。

  1. 就労許可・居留許可を継続的に取り直す必要性がなくなる。有効期限は10年。毎年3ヶ月は中国に滞在する必要あり。
  2. 投資に制限がなくなる、らしい。
  3. 行政手続で段階的に使えるようになる、中国人レーンや住宅公共積立金を使えるようになるなどの計画があるらしい
  4. コロナ禍環境下においても入境が可能とされている(2020年11月27日現在)。無ければ入境できないというわけではない。

正直なところ、現状あまりメリットはありません。オンラインサービスで身份证代わりに使えるわけでもないですし、銀行や行政での手続にも引き続きパスポートが必要だという話も聞きます。2020年から鉄道もパスポートで乗れるようになっているので、会社に就労許可・居留許可の更新を代行してもらえる駐在員としてはさほど利点はない感じです。

ただコロナ禍環境下でも入境がしやすいはずという期待と、ICチップが入っているのでいずれ第二代身份证のシステムの環に入れるのではないかという期待、また会社に頼らず家庭環境を維持できるという生活の安定性向上の観点から申請に臨みました。

これから申請する人へのアドバイス

上海での行政手続というと、情報が錯綜しているとか担当者によって言うことが違うというイメージでしたが、今は既に上海公安出入境管理のサイトで申請に必要な書類や手続が明確化されているため、大きな混乱はありませんでした。いくつか情報を補足すると:

  1. 上海総領事館で発行する婚姻証明書単体では証明として不適とのことで、婚姻証明書の発行に使った戸籍謄本のコピー2部とその翻訳文を合わせて提出する必要がありました。窓口で翻訳すると手数料100元。
  2. 上海総領事館経由で発行する無犯罪証明は自分では開封できないのですが、翻訳文もまた必要なため、申請当日に窓口で開封してもらいその場で業者に翻訳を依頼する必要がありました。手数料150元。
  3. 手続きはサイトに記載のとおり、学林路の窓口で行う必要がありました。以前は民生路だったようですが移転しています。
  4. 手数料1,800元は、申請時ではなく居留証受け取り当日に現金で支払う必要がありました。
  5. 写真は白背景の印刷したものを2枚のみ、データでの提出はありませんでした。当日窓口でも別に撮影があります。
  6. 「有效签证(或居留证件)」の原本とコピー提出をもとめられますが、居留許可カードのことではなく、パスポートに貼られたビザをコピーする必要があります。
  7. カードに中文姓名を併記するか英文姓名のみにするか選択できました。
  8. 申請時にバーコード付きの書類を渡されます。受取など後続の手続きにはこの書類の持参が必要です。

申請のクリティカル・パスは無犯罪証明です。申請から届くまできっちり2ヶ月かかりました。また婚姻証明のために日本から戸籍謄本を取り寄せる必要もあります。この2つを早めに開始するべきでしょう。

健康診断は2週間前から申込可能、1週間強で受取可能。公証は申請してから1週間で受取可能。銀行での預金凍結は即日でできます。户口本の名称変更(英文に訂正する必要あり)も、配偶者の戸籍がある場所の公安で即日でできました。配偶者の協力が必須なのは住居に関する公証と、户口本の名称変更、そして申請当日に身分証やパスポートを預かるのみです。配偶者の勤め先の名前も控えておくと良いと思います。

参考

Gradle Plugin実装の基本

Gradleは動きが早いのでTIPSを文書化する意味があまりないのですが、全体像をざっくりつかめるだけでも変化を追うには有用のはず。 そのうち体裁まとめてZennに置いたほうがいい気がしてますが、一旦走り書きということで。

Gradleの考え方

設定と実行の分離

キャッシュを効かせるためにも、Configuration Cacheとかの文脈で、設定フェーズと実行フェーズは分けて考えることを推奨する動きがあるっぽい。 実行フェーズに設定を変えるなとか、afterEvaluate { ... } に過度に依存した設定を書くなとかを気にすればいい。

設定の遅延

設定より規約

能力と慣習の分離

登場人物

プラグイン実装に必要な登場人物は、だいたい3つほど頭に入れれば良いと思われる。

  1. Plugin
  2. Extension
  3. Task

他には依存を扱うConfigurationとかProjectにメソッドやプロパティを追加するためのConventionなどもある。ConventionはExtensionで代替すべきという情報もCommunity Forumにはあったので置いといて良いかも。

Plugin

プラグインを適用した際、真っ先にインスタンスが作成されるクラス。このPluginによってまず必要な初期化処理を行う。Gradleのバージョン確認もここ。 基本的にはファイル作成やタスク作成といった重い処理は行うべきではない。適用しただけで重くなるようなプラグインになってしまうので。

また"能力と慣習の分離"の観点から、機能を提供する部分をbase pluginとして慣習・規約を扱う部分から独立して定義することが望ましい。 BasePlugin, ReportingBasePluginなど標準で用意されたbase pluginもあるので、適宜 project.pluginManager.apply(BasePlugin) などとして利用できる。

Extension

Property-like fieldsを持つPOJO。 ExtensionもPlugin同様、インスタンス生成コストは抑える方が良い。これはプラグインの提供する機能(タスク)を使わない場合でもExtensionは作成されることが多いため。

Extensionではプロジェクト内で実行されるTaskすべてに共通の設定を行うことになるはず。

Task

主にDefaultTaskを拡張Property-like fieldsを持つ。入力と出力を宣言することで、キャッシュ可能にできる。

実行のコストは重くして良い。不要なTaskは生成されない・実行されないようにビルドスクリプトを書くことがユーザには推奨されている。 同じプロジェクトに属するTaskは同時実行されない=後続Taskをブロックするので、重い処理はWorker APIに逃がすことも検討する。

lazy config をどう実現するか

PropertyFileCollection に代表されるProperty-likeなクラスを活用する。Extensionのプロパティのconventionをデフォルト値に、TaskのプロパティのconventionをExtensionのプロパティにする。ちょっと古いプラグインだとconvention mappingというのを使っているがinternal APIなので無視で良い。

// ExtensionはPOJOにする。生成コストを低く保つ。
// 抽象クラスにすることでProperty-likeフィールドの生成処理を省略できる(ドキュメント未確認)
abstract class MyExtension {
  abstract Property<String> getFoo()
}

// TaskもProperty-likeフィールドを持つ。@Inputや@Output,@Internalなどの修飾子を使ってキャッシュ可能にする
// やはり抽象クラスにすることでProperty-likeフィールドの生成処理を省略できる
@CacheableTask
abstract class MyTask extends DefaultTask {
  @Input
  abstract Property<String> getFoo()

  @TaskAction
  void run() {
    print this.foo.map { "The value of foo property is ${it}" } .get()
  }
}

// base pluginはextensionの登録やGradleのバージョン確認など、機能実行の前準備のみ実行。規約(慣習)は扱わない。
class MyBasePlugin implements Plugin<Project> {
  @Override
  public void apply(Project project) {
    def extension = project.extensions.create("myExtension", MyExtension)
    project.tasks.withType(MyTask).configureEach {
      // 規約に関係ないデフォルト値はここで設定して良いはず
      it.foo.convention(extension.foo)
    }
  }
}

// こちらのpluginでは規約(慣習)を扱う。これはsourceSetごとにTaskを作る例
class MyPlugin implements Plugin<Project> {
  @Override
  public void apply(Project project) {
    project.pluginManager.apply(MyBasePlugin)

    project.plugins.withId('java-base') {
      def convention = project.convention.getPlugin(JavaPluginConvention)
      convention.sourceSets.all {
        def name = it.getTaskName("foo", null)
        project.tasks.register(name, MyTask) {
          // 必要な設定を行う
        }
      }
    }
  }
}

VerificationTask のようなインタフェースもあるので適宜実装すると、他プラグインとの整合性を取りやすい。

ドキュメントに目を通しても迷ったら

「ドメイン駆動設計入門」付録のGradle向け解釈

ITエンジニア本大賞2021で紹介されていた「ドメイン駆動設計入門」(以下、本書と呼ぶ)が、DDDを学ぶ上でわかりやすかったです。一応他のDDD本も数冊読んではいたのですが、どうしてもユビキタス言語や境界づけられたコンテキストなど”場合による”ものが頻出してしまい、結局どうすればいいんだ……となって手が動きにくかったのです。よくわからない概念の上にさらにわからない概念を積み重ねるので、どんどん混乱する感覚がありました。

反面、本書では副題の通りボトムアップ形式を採っているため、実際にどう手を動かせば良いのか細かく確認できました。また不明点を最小化しながら進むだけでなく、その概念を導入することでどんな問題が解決されるのかが実例をもって明示されており、私のような独学派にはとてもありがたかったです。

さて本書ではC#を使っています。Javaとたいして変わらないとはいえ細かいところは違ってきますので、ここでは付録で紹介されたソリューション(プロジェクト)構築方法をGradleユーザ向けに解釈したものを紹介します。なおウェブアプリケーションフレームワークとしてspring-bootを利用するものとしますが、他のフレームワークでも大差ないはずです。

サブプロジェクトによる分割

Mavenではサブモジュール、Gradleではサブプロジェクトを使ってプロジェクトを分割します。本書ではプロジェクト分割の方針を2つ紹介していますが、ここでは簡単のためすべてのレイヤが固有サブプロジェクトを持つ構成を説明します。

domainサブプロジェクトには値オブジェクトやエンティティ、ドメインサービス、仕様やリポジトリインタフェースを配置します。ほぼPOJOで済むため、依存はspring-contextのようなアノテーションのみとなるでしょう。

ドメインオブジェクトの振る舞いの多くがここに配置されるため、Javadocやunit testを優先的に充実させる必要があるでしょう。また equals()hashCode()を手書きするとコードやテストカバレッジの見通しが悪くなることから、Immutablesのようなコードジェネレータによって対策することも考えられます。

infrastrutureサブプロジェクトはdomainサブプロジェクトに含まれるリポジトリインタフェースの実装を配置します。このサブプロジェクトはJDBCドライバのような依存先ミドルウェア固有の依存を持ちます。productionで利用する実装に加え、オンメモリで動作する実装も別途用意することで、unit testを書きやすくできます。

このサブプロジェクトではクラスをpublicにする必要はありません。Springが自動的に@Repositoryで修飾されたクラスを見つけてインスタンスを作るためです。他サブプロジェクトのunit testから呼び出す必要がある場合のみ、publicにすると良いでしょう。

applicationサブプロジェクトはdomainサブプロジェクトとinfrastructureサブプロジェクトの双方に依存します。アプリケーションサービスによるユースケースの記述をメインとするサブプロジェクトです。

本書の方針に従うならば、このサブプロジェクトに含まれるクラスやメソッドのAPIにはドメインオブジェクトを露出させないことが望ましいと言えます。このため他サブプロジェクトには api configuration ではなく implementation configuration を使って依存します。これによりドメインオブジェクトの露出をコンパイルエラーとして発見できる可能性が上がります。詳細は後述します。

presentationサブプロジェクトはapplicationサブプロジェクトにのみ依存します。後述するGradleの機能により、presentationサブプロジェクト内ではドメインオブジェクトやRepositoryの実装に触れない状態を担保できます。MockMvcなどを使うことでunit testが重くなり、またこのサブプロジェクトをビルドしている際はGradleのworkerが空きがちなので、maxParallelForksオプションでテストを並列実行することの恩恵が大きいでしょう。

apiとimplementationの使い分け

ここまでで2つ、詳細を後述すると述べたことがあります:

  1. presentationサブプロジェクトに対するドメインオブジェクトの露出をコンパイルエラーとして発見する方法
  2. presentationサブプロジェクト内ではドメインオブジェクトやRepositoryの実装に触れない状態を担保する方法

これらは表現こそ異なりますがひとつの課題を示しています。すなわち「ドメインオブジェクトをpresentationサブプロジェクトに露出させたくない」です。 もちろん依存先のpublicフィールドや戻り値がドメインオブジェクトでないことをArchUnitなどで確認しても良いですが、Gradleならこれらをコンパイルエラーとして発見する方法があります。

API and implementation separationがこの課題に対する解決になります。これはAPIとして外部に露出している依存と、そうではない内部利用している依存とを明確に分けて定義するものです。つまりサブプロジェクトが内部利用している依存は、そのサブプロジェクトの利用者に対して開示する必要はないという考えです。

これにより、applicationサブプロジェクトが依存しているdomainサブプロジェクトやinfrastructureサブプロジェクトを、presentationサブプロジェクトのコンパイル時クラスパスに含めずプロジェクトをビルドすることができます。 依存に色を付ける必要があるという意味で手間は増えますが、そもそもAPIとして露出している依存は減らすべき(JLBP-2)という話もありますので”依存がAPIとして露出しているのか?”を意識することは有用です。Gradle公式ドキュメントによるとコンパイルのパフォーマンス改善も期待できるケースがあるそうです。

例えば以上のサブプロジェクトの依存関係を図示すると、以下のようになります。

f:id:eller:20210131174527p:plain
com.savvasdalkitsis.module-dependency-graph によって生成

api configurationを使うのは1箇所だけです。infrastructureサブプロジェクトに含まれるクラスはdomainサブプロジェクトに含まれるリポジトリインタフェースを実装しており、またドメインオブジェクトをその引数や戻り値に使っているため、api configurationを使って依存します。その他はすべて implementation configurationが使えます。

補足: アセット生成を独立したサブプロジェクトの責務とする理由

前出の図に含まれているfrontendサブプロジェクトは、HTMLやJSなどのアセットを生成するためのものです。ドメイン駆動からは外れますが解説します。

このサブプロジェクトだけ若干特殊で、npmやyarnのプロジェクトをfrontend-gradle-pluginを通じてビルドする作りになります。ReactやVueの開発をGradleで無理やり実現するのではなくnpmあるいはyarnを呼び出す形を取ることで、JavaScript開発の知見を無理なく導入するとともにフロントエンド開発者にGradleやJavaの学習を強要する必要性を減らせます。

ちなみにビルド性能の向上も期待できます。これはfrontend-gradle-pluginのタスク、特にinstallFrontendタスクが重いことと、Gradleは異なるプロジェクトに属するタスクだけparallel buildできることから、説明できます。なおプロジェクトと同数以上のworkerがいないと性能が出ないので、--max-workersオプションを使うかorg.gradle.workers.maxプロパティを使って明示的にワーカー数を引き上げておくことが望ましいです。以下に手元の環境でhyperfineした結果を貼っておきます:

Benchmark #分割前: ./gradlew clean build
  Time (mean ± σ):     28.356 s ±  2.624 s    [User: 1.419 s, System: 0.145 s]
  Range (min … max):   25.914 s … 33.624 s    10 runs

Benchmark #分割後 (4 workers): ./gradlew clean build
  Time (mean ± σ):     27.161 s ±  3.987 s    [User: 1.388 s, System: 0.137 s]
  Range (min … max):   23.202 s … 33.721 s    10 runs

Benchmark #分割後 (5 workers): ./gradlew clean build --max-workers=5
  Time (mean ± σ):     24.442 s ±  2.916 s    [User: 1.321 s, System: 0.135 s]
  Range (min … max):   21.799 s … 31.388 s    10 runs

まとめ

ドメイン駆動設計入門」付録のGradle向け解釈を述べました。本書は私のような、ドメイン駆動を手を動かしつつ独学したい方におすすめします。

本投稿が本書の内容を実践する上でGradleユーザの参考になれば幸いです。またGitHubに私の書いたプロジェクトを置いてありますので、具体的にプロジェクト設定を見てみたい方は参照ください。

最近見かける新しいライセンスについて

Elastic社のブログをきっかけに、最近見かける新しいライセンスについて個人的に調べてみた。私は専門家ではないので要注意。公開情報も隅々まで追えているわけではないし。

なお一部ライセンスはOpen Source Initiative (OSI)による承認を受けていないので、ここではオープンソースライセンスではなく単に「ライセンス」と書くことにする。

新しいライセンスが誕生している背景

  • 従来のオープンソースライセンスが再頒布以外の利用をあまり想定していなかった。
  • Open-core modelないし完全オープンソース戦略を採る企業が自衛策を必要とした。
  • 既存のライセンスが難解なため、理解しやすいライセンスが求められた。
  • OSS活動を収入に繋げるためのモデルが試行錯誤されている。

新しいライセンスを導入しているプロジェクト(一例)

プロジェクト ライセンス
Elastic SSPLと独自ライセンス
MongoDB SSPL
CockroachDB BSL *1
Redis Modules 独自ライセンス
MariaDB BSL
Artifex AGPL3と独自ライセンス
Husky v5 License Zero Parity 7.0.0 and MIT (contributions) with exception License Zero Patron 1.0.0

各種ライセンス

GNU Affero General Public License (AGPL3)

言うほど新しくない。2010年に公開されたnippondanji氏のスライドを参照。

Business Source License (BSL)

MariaDBが作成、v1.1が最新。以下説明文をサイトから引用:

BSL is a new alternative to Closed Source or Open Core licensing models. Under BSL, the source code is freely available from the start and it is guaranteed to become Open Source at a certain point in time (i.e., the Change Date). Usage below a specific level in the BSL is always completely free. Usage above the specified level requires a license from the vendor until the Change Date, at which point all usage becomes free.

一定期間後にオープンソースになる(MaxScaleならGPLのv2以降になる)ということは、旧バージョンのサポートをユーザ自身が行うことも他の企業が請け負うことも可能ということ。最新バージョンの開発に注力したいLicensorと、サポートが切れたときの対応を懸念するユーザとの双方に利がありそう。以下のQ&Aはこれを意図しての記載と思われる。

Q: What are the main benefits for a company using BSL for their product?

A: BSL allows anyone to work on the code, both for their own benefit and that of others. It also generates trust in the BSL product, as there is no lock-in to a single software vendor.

このときusage limitsの定義はLicensorに委ねられている。例えばMaxScaleだとサーバ台数で絞っていて、2台までならどのような用途でも制限なく利用可能。このため”自衛策”になると期待される。

ちょっと気になるのはChange Dateの変更タイミングで、FAQには以下のようにメジャーバージョンごとに設定するものとある。マイナーバージョンごとにEOLを設定したいケースもあると思うのだけど。

Q: Will the Change Date remain constant?

A: No. Each new major version of the software will have its own Change Date.

Server Side Public License (SSPL)

今回Elasticが採用したライセンスで、MongoDBも利用している。GPLv3をベースにしているらしい

AGPLv3をベースにしなかった理由として、AGPLv3のRemote Network Interactionの定義が曖昧で市場に混乱を生んでいたと書いてある。GNUのFAQを見ても、確かに厳密性は低いと感じる。ユーザのリクエストを直接受けない、ユーザにレスポンスを直接返さないと解釈・主張できるケースは多数ありそう。なおAGPLv3とSSPLの違いがMongoDB公式サイトで配布されているので、具体的な違いを容易に確認できる。

ソースコードを変更することなく自社サービスのコンポーネントとして提供する場合は、特に制約はない。MongoDBのFAQにも以下の記載がある:

Q: What are the implications of this new license on applications built using MongoDB and made available as a service (SaaS)?

A: The copyleft condition of Section 13 of the SSPL applies only when you are offering the functionality of MongoDB, or modified versions of MongoDB, to third parties as a service. There is no copyleft condition for other SaaS applications that use MongoDB as a database.

プログラムの機能自体をサービスとして提供する場合は、そのサービスのソースコードを公開する必要がある。ここでリンクの有無やユーザにレスポンスを返すかとかは関係なく、"the Service Source Code"すべてが対象になっているのに注目。MongoDBのFAQ "What specifically is the difference between the GPL and the SSPL?" では以下が対象になると書いてある:

the software it uses to offer such service under the terms of the SSPL, including the management software, user interfaces, application program interfaces, automation software, monitoring software, backup software, storage software and hosting software, all such that a user could run an instance of the service using the source code made available.

無理ゲー感満載である。もしここで言うsoftwareがクラウドベンダーが持つIaaS/PaaSをも対象とするなら、事実上不可能と言えそう。

この制約はオープンソースライセンスとしては強すぎるようで、OSIからOpen Software Definitionに明確に不適合だと言われているそう。 21日にはOSIからThe SSPL is Not an Open Source Licenseという声明も出ている。

Parity Public License

Husky v5が採用しているライセンス。7.0.0が最新。GitHubで各種ドキュメントが公開されている。あまりアクティブじゃなさそうというか、さほど導入実績がなさそうというか、Huskyもよく使う気になったなぁという感じ。READMEにはアーリーアクセスが完了したらMITに戻すとも書いてあるので、試用という立ち位置なのだろうか。

無償で自由に使えるし共有も可能だが、ライセンスされたソフトウェアを使ってビルドされたソフトウェア(software that builds on it)もまた同様に公開されなければならない。このbuilds on itという表現が珍しい。Huskyで使われているということは、ライブラリとしてリンクしたソフトウェアだけではなく、このソフトウェアをdevDependenciesに入れて利用したすべてのソフトウェアに対して効力があるものと推測される。ホントか?

またライセンスにDon’t make any legal claimという文が入っているのも気になる。Facebookが”BSD + Patents”でやらかしたのと何が違うんだろう。ここまでくるとIANALなのでさっぱりわからない。

Patron License

Husky v5が採用しているライセンス。1.0.0が最新。他のライセンスと組み合わせて使う。 支払いを行うことで他のライセンスが持つnoncommercial or copyleftの制約を外すことができる。つまり商用利用やソースコード改変がしたかったら支払いをするように、ということ。

Husky v5は自身のライセンスを「License Zero Parity 7.0.0 and MIT (contributions) with exception License Zero Patron 1.0.0」としているが、整理すると:

ということらしい。Huskyは2年前からnpm install時に寄付を呼びかけていたので、金銭的支援を得ることがライセンス導入の主目的と思われる。サービスではないOSSが選択可能な収益につながるライセンスは現状他になさそうなので、Huskyが先鞭をつけてくれるといいなぁ。

結局こうしたライセンスは勝手に "as a Service" されることへの自衛策になるのか

  • BSLを適用することで、サーバ台数などに制限をかけることができそう。Elasticの語る将来の計画を見る限りでは、有償モジュールの代替機能開発・利用を禁止する拘束力を持たせることも可能と見ているのかも。
  • 完全にソースコードを公開しているプロジェクトの場合、AGPLを適用しても"as a Service"への抑止力は得られないが、サービス提供側に改変内容を公開する必要性が生じる。それでも大規模なインフラを持っているクラウドベンダーに目をつけられると競争力を発揮できなさそう。
  • Open-core戦略を採るプロジェクトの場合、AGPLを適用しても"as a Service"への抑止力は得られないが、サービス提供側に改変内容を公開する必要性が生じる。非公開モジュールと同じ機能を持つモジュールをクラウドベンダーが実装し"as a Service"を実現することに対する抑止力をどう持たせるかが課題か?
  • SSPLを適用することで、プログラムをコンポーネントとして使うユーザの自由を確保しつつ、"as a Service"への抑止力を得ることができる。
  • Patron Licenseは商用利用自体を制限するライセンスと組み合わせなければならず、"as a Service"だけを制限したい場合には使いにくそう。

揺らぐオープンソースの定義

これらのライセンスを適用すると厳密な意味でのオープンソースではなくなるわけだが、より強力かつ実情に沿ったコピーレフトを実現するという意味でも、オープンソースの定義自体が更新を必要としていると見ることもできそう。"as a Service"を自由に提供できるのも、ひとつのopennessではあるわけで。

ライセンスが乱立しても良いことはないので、OSIなどの団体がいずれBSLなどを追認するような流れになると良いなぁと思う。

以上。間違っている箇所があれば、はてなブックマークTwitterでご指摘いただけるとありがたい。

更新履歴

2020年のFOSS活動状況まとめ

昨年のに引き続きFOSS活動状況をまとめます。2020年12月21日時点の情報です。

概要:昨年比23%増

GitHubのプロファイルページによると今年のpublic contributionsは1,440で、昨年が1,166だったので約23%増です。commit 69%のpull requests 16%なので、引き続き手を動かしてコードを書けたと思います。

f:id:eller:20201221181440p:plain

また今年からGitHub Enterprise Cloud(GHEC)の業務利用を始めたのですが、publicとprivatecontributions比はだいたい趣味:業務が7:3でした。業務の方に手を動かす余地があると考えるべきか、マネジメントなのにこんなに手を動かしてることを反省点として受け止めるか……。

今年の主なリリースはspotbugs-gradle-plugin 4.0.0~4.6.0、SpotBugs の4.0.0 beta5~4.2.0、あといくつかGitHub ActionをTypeScriptで実装しました

SpotBugs周りの開発

今年はSpotBugs v4の安定版を出したことが1番大きいリリースでした。加えてGradleの非公開APIに依存してしまってメンテナンス性と性能の双方に問題が出ていたGradleプラグインをスクラッチで書き直したのも大きなプロジェクトだったと言えます。

SpotBugs v4がFindBugsよりも性能上で改善されていることはマイクロベンチで確認しましたし、MavenプラグインやGradleプラグインに加えてGitHub Actionもあるらしいので、特にFindBugsで困ってないという方も一度アップグレードを検討してみると良いと思います。

その他

この夏ごろにSLF4Jがメンテナンスされるのか知る目的で、チケットやPRの整理をしたりMLにメールを送ったりという活動をしばらく続けていました。私個人の結論としては1.8は使うべきではないし2.0も先行き不透明なので、今後は極力Log4j 2をLogging Facadeとして使うべきだろうと考えています。

OASISの定めるSARIFに興味を持ってSpotBugsレポートとしての実装を進めています。JSONスキーマの制約から望ましい実装ができていない状態が続いているので、いずれテコ入れしたいところです。

TypeScriptでGitHub Actionを書くときのTips

actions-setup-docker-compose, sonar-update-center-actionsauce-connect-actionなど5つくらいActionを実装したので、Tipsをここにまとめます。

公式テンプレートを使う

TypeScriptでGitHub Actionを書くためのテンプレートが公開されています。とりあえずこのテンプレートを使うのがおすすめです。

github.com

使っているのはnpm, jest, @vercel/ncc, eslint, prettier とオーソドックスなもの。jest が気に入らなければ置き換えても構わないでしょう。

公式ドキュメントとライブラリに目を通す

さらっとで良いので以下のページは読んでおくといいです。どういった情報がどこにあるのか、これをするために何を使えばいいのか、情報の場所を掴んでおきます。

特に以下は必要になる知識だと思われます:

Fixtureの作成と読込

単体テストでは api.github.com との通信を再現してコードの挙動を見ていくことになります。一応 docs.github.com に期待されるステータスコードやそのペイロードが書かれていますが、まだ内容が間違っていることがあったので実際にコードを回して確認することを薦めます。ドキュメントよりは @octokit/rest の型情報のほうが信用できます。

レスポンスを再現するためのFixtureは、JSON.stringify(response.data)JSONファイルに保存して作成します。tsconfig.json"resolveJsonModule": trueを設定すればJSONファイルをそのままオブジェクトとしてimportできるので便利です。

// quoted from https://git.io/JIchk
import releases from './fixtures/sonarqube-releases.json'

const token = process.env.GITHUB_TOKEN
if (!token) {
  throw new Error('No GITHUB_TOKEN env var found')
}

test('searchLatestMinorVersion()', async () => {
  const scope = nock('https://api.github.com')
    .get('/repos/SonarSource/sonarqube/releases')
    .reply(200, releases)
  expect(await searchLatestMinorVersion(token)).toBe('8.5.*')
})

こうしたテストの面倒を見てくれる@octokit/fixtures もあるようですが、私はまだnockしか使っていないので詳しいことは不明です。

Changelog Blogを購読する

Actions周りはまだ動きが活発で、最近も add-path コマンドなどが削除されたばかりです。 変更に追従するためにもChangelog Blogは購読をすると良いでしょう。

TBU: まだ自動化できていないこと

  • dependabot が依存を更新したら dist ディレクトリ以下のファイルも更新する
    • pull_request イベントでActionを発火させて、 npm run all した結果をcommit & pushするようにすればできるはず

技術書「JavaのビルドとCIのキホン」を公開しました

zenn.dev がホットなのでフォロワーの皆様にアンケートを取った結果、「JavaのビルドとCIのキホン」が5票を獲得したので書きました。

書籍はこちらです。

zenn.dev

zenn.dev上では約26266文字と言われてるんですが、いや流石にそんなに書いてないはず。まえがきと付録を除いて6章構成です。 初心者向けを意図していますが、経験者でないとわからない論理的飛躍というか、結果ありきの書き方になっているところが残っているかもしれません。しばらくは継続的に更新します。