GradleタスクがOne of setGitDir or setWorkTree must be called.というエラーで異常終了した

2022年7月に新しいPCをセットアップしたときに、GradleのKotlinプロジェクトがビルドできなくてハマったのでメモ。
だいぶ時間が経ってしまったが...。

 

 

[JGitのエラー]

新しいPCをもらって、JDKをインストールして、Gitをインストールして、ソースコードをクローンした。
そして./gradlew buildを実行したところ、次のエラーがでた。

* What went wrong:
Execution failed for task ':getVersion'.
> One of setGitDir or setWorkTree must be called.

 

問題の箇所のGradleスクリプトは以下のように書かれていた。

import org.eclipse.jgit.api.Git
import org.eclipse.jgit.internal.storage.file.FileRepository
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

val gitFolder: File by lazy {
    FileRepositoryBuilder().findGitDir().build().directory
}

val getVersion by tasks.registering(WriteProperties::class) {
    outputFile = file("src/main/resources/version.properties")
    outputs.upToDateWhen { false }
    doFirst {
        FileRepository(gitFolder).use {
            Git(it).use { git ->
                val head = git.log().setMaxCount(1).call().first()
                val commitTime = head.authorIdent.let { time ->
                    ZonedDateTime.ofInstant(time.`when`.toInstant(), ZoneId.of("Asia/Tokyo"))
                }.format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmm"))
                val tags = git.tagList().call()
                    .filter { tag -> tag.objectId == head.id }
                    .map { tag -> tag.name.removePrefix("refs/tags/") }
                property("VERSION_DATE", commitTime)
                property("VERSION_BUILD", head.id.name.take(8))
                property("TAGS", tags.joinToString(","))
            }
        }
    }
}

 

エラーメッセージでググると、Stackoverflowの質問が出てきた。これによると、JVM言語でGit操作を行うためのJGitというライブラリで発生しているエラーのようだ*1

stackoverflow.com

 

findGitDir()を呼び出しているあたりからソースコードを追っていくと、以下の行あたりで、カレントディレクトリがGitリポジトリ扱いされていないらしいことが分かった。

github.com

public B findGitDir() {
	if (getGitDir() == null)
		findGitDir(new File("").getAbsoluteFile());
	return self();
}

public B findGitDir(File current) {
	if (getGitDir() == null) {
		FS tryFS = safeFS();
		while (current != null) {
			File dir = new File(current, DOT_GIT);
			if (FileKey.isGitRepository(dir, tryFS)) {
				setGitDir(dir);
				break;
			}

			current = current.getParentFile();
			if (current != null && ceilingDirectories != null
					&& ceilingDirectories.contains(current))
				break;
		}
	}
	return self();
}

 

[デバッグと解決策]

Gradleスクリプトの問題のありそうな箇所の前後で、同じようにカレントディレクトリを出力してデバッグしてみると、Gradleのビルドが成功する環境と失敗する環境で差分があることが分かった。

val gitFolder: File by lazy {
    val b = FileRepositoryBuilder()
    b.findGitDir()
    println("DEBUG: " + b.gitDir)
    println("DEBUG: " + File("").getAbsoluteFile())
    FileRepositoryBuilder().findGitDir().build().directory
}

// 正常にビルドできる環境(旧PC)の出力結果
DEBUG: D:\my-project\.git
DEBUG: D:\my-project

// ビルドが失敗する環境(新PC)の出力結果
DEBUG: null
DEBUG: C:\Users\kdnakt\.gradle\daemon\6.3

 

どうやら、カレントディレクトリがビルドを実行しているプロジェクトのディレクトリではなく、Gradleのインストールディレクトリになってしまっているのが問題のようだ。でもなぜ?
2つの環境の差異を探してGradleのビルドスクリプトで利用されている環境変数を1つずつ比較してみたが、意味のある差分は見つけられなかった。

 

自分は他のプロジェクトも並行して取り組んでいたため解決策に辿り着けなかったが、同じプロジェクトに取り組んでいる同僚O氏が、ちょうど同じ時期に同じ問題にぶつかって、解決策を見つけてくれた。
曰く、最新のJDKをインストールすると解決したとのこと。

 

[JDK 1.8.0_342の問題]

JDKのバージョン更新で解決したとのことなので、改めて使っていたバージョンを確認する。
問題の起きていた新PCで使っていたJavaAmazon Correttoで、バージョンは以下の通り。

openjdk version "1.8.0_342"
OpenJDK Runtime Environment Corretto-8.342.07.1 (build 1.8.0_342-b07)
OpenJDK 64-Bit Server VM Corretto-8.342.07.1 (build 25.342-b07, mixed mode)

 

以前のPCでは以下のバージョンで問題なく動いていた。

openjdk version "1.8.0_312"
OpenJDK Runtime Environment Corretto-8.312.07.1 (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM Corretto-8.312.07.1 (build 25.312-b07, mixed mode)

 

調べてみると、こちらのJava 11のチケットが見つかった。

bugs.openjdk.org

同じ修正がJava 8にバックポートされたのがopenjdk8u342というバージョン。正確にはそのビルドb01。Correttoで言うと1.8.342.07.1でまさに自分が利用しているバージョンになる。

bugs.openjdk.org

 

修正内容は以下の通りでFileSystemクラスのgetUserPath()メソッドの実装でSecurityManagerが呼ばれるようになっている。

github.com

 

この変更について、問題点を指摘するチケットがこちら。まさにGradleとかで問題が起きるよ、と言われている。

Changing the "user.dir" was not recommended from the beginning but it was not forbidden, so there are some old applications that rely on the old behavior. One of the app which sets the "user.dir" is Gradle. The Gradle has a notice in the documentation for the user:
"Never use new File(relative path) because this creates a path relative to the current working directory (CWD). Gradle can make no guarantees about the location of the CWD, which means builds that rely on it may break at any time".

But for compatibility reasons they still set the "user.dir" property, so the old plugins will work.
Now that compatibility is broken due to the fix I mention. We found such apps immediately after the release.

I think we should roll it back as soon as possible.

bugs.openjdk.org

 

2022年7月22日に修正のプルリクエストがOpenJDKのリポジトリで提出され、Amazon Correttoは翌2022年7月23日に、8.342.07.3として同じ修正がリリースされている。

github.com

 

バージョン番号が同じだけど、ビルド番号(リリースバージョン)が違うと結構中身が違うものだな、と痛感した一件だった。

 

[まとめ]

  • Amazon Corretto 1.8.0_342.07.1を使っていたら、Gradleでのビルド時にuser.dirプロパティを利用するスクリプトが動作しなくなった
  • user.dirプロパティの利用にセキュリティポリシーの設定が必要となったことが原因
  • Amazon Corretto 1.8.0_342.07.3で問題が修正されていた

*1:JGitについてはこちら。

git-scm.com