Git を契約やスタブのストレージとして使用するにはどうすればよいですか ?

多言語の世界には、Artifactory や Nexus のようにバイナリストレージを使用しない言語があります。Spring Cloud Contract バージョン 2.0.0 からは、契約とスタブを SCM (ソース管理) リポジトリに保存するメカニズムが提供されます。現在サポートされている SCM は Git のみです。

リポジトリには次の設定が必要です (​ ここ [GitHub] (英語) からチェックアウトできます)。

.
└── META-INF
    └── com.example
        └── beer-api-producer-git
            └── 0.0.1-SNAPSHOT
                ├── contracts
                │   └── beer-api-consumer
                │       ├── messaging
                │       │   ├── shouldSendAcceptedVerification.groovy
                │       │   └── shouldSendRejectedVerification.groovy
                │       └── rest
                │           ├── shouldGrantABeerIfOldEnough.groovy
                │           └── shouldRejectABeerIfTooYoung.groovy
                └── mappings
                    └── beer-api-consumer
                        └── rest
                            ├── shouldGrantABeerIfOldEnough.json
                            └── shouldRejectABeerIfTooYoung.json

META-INF フォルダーの下:

  • アプリケーションは groupId ( com.example など) ごとにグループ化されます。

  • 各アプリケーションは、その artifactId (たとえば、beer-api-producer-git) によって表されます。

  • 次に、各アプリケーションはバージョンごとに整理されます ( 0.0.1-SNAPSHOT など)。Spring Cloud Contract バージョン 2.1.0 以降、次のようにバージョンを指定できます (バージョンがセマンティックバージョニングに従っていることを前提としています)。

    • + または latest: スタブの最新バージョンを検索するには (スナップショットが常に、指定されたリビジョン番号の最新のアーティファクトであると仮定します)。つまり:

      • 1.0.0.RELEASE2.0.0.BUILD-SNAPSHOT2.0.0.RELEASE をお持ちの場合、最新のものは 2.0.0.BUILD-SNAPSHOT であると想定します。

      • 1.0.0.RELEASE および 2.0.0.RELEASE がある場合、最新のものは 2.0.0.RELEASE であると想定されます。

      • latest または + というバージョンがある場合は、そのフォルダーが選択されます。

    • release: スタブの最新リリースバージョンを検索します。つまり:

      • 1.0.0.RELEASE2.0.0.BUILD-SNAPSHOT2.0.0.RELEASE をお持ちの場合、最新のものは 2.0.0.RELEASE であると想定されます。

      • release というバージョンがある場合は、そのフォルダーが選択されます。

最後に、次の 2 つのフォルダーがあります。

  • contracts: 各コンシューマーが必要とする契約をコンシューマー名 ( beer-api-consumer など) のフォルダーに保存することをお勧めします。これにより、stubs-per-consumer 機能を使用できるようになります。さらなるディレクトリ構造は任意です。

  • mappings: Maven または Gradle Spring Cloud Contract プラグインは、スタブサーバーマッピングをこのフォルダーにプッシュします。コンシューマー側では、スタブランナーがこのフォルダーをスキャンして、スタブ定義を使用してスタブサーバーを起動します。フォルダー構造は、contracts サブフォルダーに作成されたもののコピーです。

プロトコル規約

契約のソースの型と場所 (バイナリストレージまたは SCM リポジトリ) を制御するには、リポジトリの URL でプロトコルを使用できます。Spring Cloud Contract は、登録されたプロトコルリゾルバーを反復処理し、(プラグインを使用して) 契約または (Stub Runner から) スタブをフェッチしようとします。

SCM 機能については、現在、Git リポジトリをサポートしています。これを使用するには、リポジトリ URL を配置する必要があるプロパティで、接続 URL の先頭に git:// を付ける必要があります。次のリストはいくつかの例を示しています。

git://file:///foo/bar
git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
git://[email protected] (英語)  :spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git

プロデューサー

プロデューサーにとっては、SCM (ソース管理管理) アプローチを使用するために、外部契約に使用するものと同じメカニズムを再利用できます。git:// プロトコルで始まる URL から SCM 実装を使用するように Spring Cloud Contract をルーティングします。

pushStubsToScm ゴールを Maven に手動で追加するか、Gradle で pushStubsToScm タスクを使用 (バインド) する必要があります。スタブを Git リポジトリの origin にプッシュしません。

次のリストには、Maven ビルドファイルと Gradle ビルドファイルの両方の関連部分が含まれています。

  • Maven

  • Gradle

<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>${spring-cloud-contract.version}</version>
    <extensions>true</extensions>
    <configuration>
        <!-- Base class mappings etc. -->

        <!-- We want to pick contracts from a Git repository -->
        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>

        <!-- We reuse the contract dependency section to set up the path
        to the folder that contains the contract definitions. In our case the
        path will be /groupId/artifactId/version/contracts -->
        <contractDependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>${project.artifactId}</artifactId>
            <version>${project.version}</version>
        </contractDependency>

        <!-- The contracts mode can't be classpath -->
        <contractsMode>REMOTE</contractsMode>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <!-- By default we will not push the stubs back to SCM,
                you have to explicitly add it as a goal -->
                <goal>pushStubsToScm</goal>
            </goals>
        </execution>
    </executions>
</plugin>
contracts {
	// We want to pick contracts from a Git repository
	contractDependency {
		stringNotation = "${project.group}:${project.name}:${project.version}"
	}
	/*
	We reuse the contract dependency section to set up the path
	to the folder that contains the contract definitions. In our case the
	path will be /groupId/artifactId/version/contracts
	 */
	contractRepository {
		repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
	}
	// The mode can't be classpath
	contractsMode = "REMOTE"
	// Base class mappings etc.
}

/*
In this scenario we want to publish stubs to SCM whenever
the `publish` task is invoked
*/
publish.dependsOn("publishStubsToScm")

publishStubsToScm gradle タスクをさらにカスタマイズすることもできます。次の例では、ローカル git リポジトリから契約を選択するようにタスクがカスタマイズされています。

gradle
publishStubsToScm {
	// We want to modify the default set up of the plugin when publish stubs to scm is called
	// We want to pick contracts from a Git repository
	contractDependency {
		stringNotation = "${project.group}:${project.name}:${project.version}"
	}
	/*
	We reuse the contract dependency section to set up the path
	to the folder that contains the contract definitions. In our case the
	path will be /groupId/artifactId/version/contracts
	 */
	contractRepository {
		repositoryUrl = "git://file://${new File(project.rootDir, "../target")}/contract_empty_git/"
	}
	// We set the contracts mode to `LOCAL`
	contractsMode = "LOCAL"
	}
IMPORTANT

2.3.0.RELEASE 以降、以前 publishStubsToScm カスタマイズに使用されていた customize{} クロージャーは使用できなくなりました。前の例のように、設定は publishStubsToScm クロージャ内で直接適用する必要があります。

このような設定では:

  • git プロジェクトが一時ディレクトリにクローン作成されます

  • SCM スタブダウンローダーは、META-INF/groupId/artifactId/version/contracts フォルダーに移動して契約を検索します。例: com.example:foo:1.0.0 の場合、パスは META-INF/com.example/foo/1.0.0/contracts になります。

  • テストは契約から生成されます。

  • スタブは契約から作成されます。

  • テストに合格すると、スタブは複製されたリポジトリにコミットされます。

  • 最後に、プッシュがそのリポジトリの origin に送信されます。

契約がローカルに保存されているプロデューサー

SCM をスタブと契約の宛先として使用するもう 1 つのオプションは、契約をプロデューサーとともにローカルに保存し、契約とスタブのみを SCM にプッシュすることです。次のプロジェクト (英語) は、 Maven および Gradle でこれを実現するために必要なセットアップを示しています。

このような設定では:

  • デフォルトの src/test/resources/contracts ディレクトリからの契約が選択されます。

  • テストは契約から生成されます。

  • スタブは契約から作成されます。

  • テストに合格したら:

    • git プロジェクトが一時ディレクトリに複製されます。

    • スタブと契約は、複製されたリポジトリにコミットされます。

  • 最後に、そのリポジトリの origin に対してプッシュが行われます。

プロデューサーとの契約とスタブを外部リポジトリに保持する

契約をプロデューサーリポジトリに保持し、スタブを外部 git リポジトリに保持することもできます。これは、基本的なコンシューマーとプロデューサーのコラボレーションフローを使用したいが、スタブを保存するためにアーティファクトリポジトリを使用できない場合に最も役立ちます。

これを行うには、通常のプロデューサー設定を使用してから、pushStubsToScm ゴールを追加し、スタブを保持するリポジトリに contractsRepositoryUrl を設定します。

コンシューマー

コンシューマー側では、@AutoConfigureStubRunner アノテーション、JUnit 4 ルール、JUnit 5 拡張機能、またはプロパティのいずれかから repositoryRoot パラメーターを渡すときに、git:// プロトコルをプレフィックスとして付けた SCM リポジトリの URL を渡すことができます。次の例は、その方法を示しています。

@AutoConfigureStubRunner(
    stubsMode="REMOTE",
    repositoryRoot="git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
    ids="com.example:bookstore:0.0.1.RELEASE"
)

このような設定では:

  • git プロジェクトが一時ディレクトリに複製されます。

  • SCM スタブダウンローダーは META-INF/groupId/artifactId/version/ フォルダーに移動して、スタブ定義と契約を検索します。例: com.example:foo:1.0.0 の場合、パスは META-INF/com.example/foo/1.0.0/ になります。

  • スタブサーバーが起動され、マッピングが提供されます。

  • メッセージング定義は、メッセージングテストで読み取られて使用されます。