[[how-to-use-git-as-storage]]
= How Can I Use Git as the Storage for Contracts and Stubs?

include::partial$_attributes.adoc[]

In the polyglot world, there are languages that do not use binary storage, as
Artifactory and Nexus do. Starting from Spring Cloud Contract version 2.0.0, we provide
mechanisms to store contracts and stubs in a SCM (Source Control Management) repository. Currently, the
only supported SCM is Git.

The repository would have to have the following setup
(which you can checkout from https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/{samples_branch}/contracts_git/[here]):

[source,indent=0]
----
.
└── 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
----

Under the `META-INF` folder:

* We group applications by `groupId` (such as `com.example`).
* Each application is represented by its `artifactId` (for example, `beer-api-producer-git`).
* Next, each application is organized by its version (such as `0.0.1-SNAPSHOT`). Starting
from Spring Cloud Contract version `2.1.0`, you can specify the versions as follows
(assuming that your versions follow semantic versioning):
** `+` or `latest`: To find the latest version of your stubs (assuming that the snapshots
are always the latest artifact for a given revision number). That means:
*** If you have `1.0.0.RELEASE`, `2.0.0.BUILD-SNAPSHOT`, and `2.0.0.RELEASE`, we assume
that the latest is `2.0.0.BUILD-SNAPSHOT`.
*** If you have `1.0.0.RELEASE` and `2.0.0.RELEASE`, we assume that the latest is `2.0.0.RELEASE`.
*** If you have a version called `latest` or `+`, we will pick that folder.
** `release`: To find the latest release version of your stubs. That means:
*** If you have `1.0.0.RELEASE`, `2.0.0.BUILD-SNAPSHOT`, and `2.0.0.RELEASE` we assume
that the latest is `2.0.0.RELEASE`.
*** If you have a version called `release`, we pick that folder.

Finally, there are two folders:

* `contracts`: The good practice is to store the contracts required by each
consumer in the folder with the consumer name (such as `beer-api-consumer`). That way, you
can use the `stubs-per-consumer` feature. Further directory structure is arbitrary.
* `mappings`: The Maven or Gradle Spring Cloud Contract plugins push
the stub server mappings in this folder. On the consumer side, Stub Runner scans this folder
to start stub servers with stub definitions. The folder structure is a copy
of the one created in the `contracts` subfolder.

[[how-to-protocol-convention]]
== Protocol Convention

To control the type and location of the source of contracts (whether
binary storage or an SCM repository), you can use the protocol in the URL of
the repository. Spring Cloud Contract iterates over registered protocol resolvers
and tries to fetch the contracts (by using a plugin) or stubs (from Stub Runner).

For the SCM functionality, currently, we support the Git repository. To use it,
in the property where the repository URL needs to be placed, you have to prefix
the connection URL with `git://`. The following listing shows some examples:

[source,indent=0]
----
git://file:///foo/bar
git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
----

[[how-to-protocol-convention-producer]]
== Producer

For the producer, to use the SCM (Source Control Management) approach, we can reuse the
same mechanism we use for external contracts. We route Spring Cloud Contract
to use the SCM implementation from the URL that starts with
the `git://` protocol.

IMPORTANT: You have to manually add the `pushStubsToScm`
goal in Maven or use (bind) the `pushStubsToScm` task in
Gradle. We do not push stubs to the `origin` of your git
repository.

The following listing includes the relevant parts both Maven and Gradle build files:

Maven::
+
[source,xml,indent=0,role="primary"]
----
<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>
----

Gradle::
+
[source,groovy,indent=0,role="secondary"]
----
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")
----

You can also further customize the `publishStubsToScm` gradle task. In the following example,
the task is customized to pick contracts from a local git repository:

[source,groovy,indent=0]
.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:: Starting with the `2.3.0.RELEASE`, the `customize{}` closure previously used for the
`publishStubsToScm` customization is no longer available. The settings should be applied directly
within the `publishStubsToScm` closure, as in the preceding example.

With such a setup:

* A git project is cloned to a temporary directory
* The SCM stub downloader goes to the `META-INF/groupId/artifactId/version/contracts` folder
to find contracts. For example, for `com.example:foo:1.0.0`, the path would be
`META-INF/com.example/foo/1.0.0/contracts`.
* Tests are generated from the contracts.
* Stubs are created from the contracts.
* Once the tests pass, the stubs are committed in the cloned repository.
* Finally, a push is sent to that repo's `origin`.

[[how-to-protocol-convention-producer-with-contracts-stored-locally]]
== Producer with Contracts Stored Locally

Another option to use the SCM as the destination for stubs and contracts is to store the
contracts locally, with the producer, and only push the contracts and the stubs to SCM.
The following {samples_url}/producer_with_empty_git/[project] shows the setup required to achieve this with Maven and Gradle.

With such a setup:

* Contracts from the default `src/test/resources/contracts` directory are picked.
* Tests are generated from the contracts.
* Stubs are created from the contracts.
* Once the tests pass:
** The git project is cloned to a temporary directory.
** The stubs and contracts are committed in the cloned repository.
* Finally, a push is done to that repository's `origin`.

[[how-to-protocol-convention-contracts-producer-stubs-external]]
== Keeping Contracts with the Producer and Stubs in an External Repository

You can also keep the contracts in the producer repository but keep the stubs in an external git repository.
This is most useful when you want to use the base consumer-producer collaboration flow but cannot
use an artifact repository to store the stubs.

To do so, use the usual producer setup and then add the `pushStubsToScm` goal and set
`contractsRepositoryUrl` to the repository where you want to keep the stubs.

[[how-to-protocol-convention-contracts-producer-stubs-external-consumer]]
== Consumer

On the consumer side, when passing the `repositoryRoot` parameter,
either from the `@AutoConfigureStubRunner` annotation, the
JUnit 4 rule, JUnit 5 extension, or properties, you can pass the URL of the
SCM repository, prefixed with the `git://` protocol. The following example shows how to do so:

[source,java,indent=0]
----
@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"
)
----

With such a setup:

* The git project is cloned to a temporary directory.
* The SCM stub downloader goes to the `META-INF/groupId/artifactId/version/` folder
to find stub definitions and contracts. For example, for `com.example:foo:1.0.0`, the path would be
`META-INF/com.example/foo/1.0.0/`.
* Stub servers are started and fed with mappings.
* Messaging definitions are read and used in the messaging tests.

