# Publish Gradle artifacts to CloudRepo

> Publish a Java library to a CloudRepo Maven repository with Gradle's maven-publish plugin and a repository token, check that it landed, use the token in CI, and fix a 401, 403, 409 or 413.

Gradle publishes to CloudRepo through its `maven-publish` plugin: CloudRepo is a Maven repository, so Gradle needs the same repository URL and the same repository token as Maven. This page is for the person who publishes a build. To use what you published, see [Gradle and Maven repositories](/docs/formats/maven.html).

## Before you start

- A Maven repository. If you have none, [create one](/docs/manage/repositories.html#creating-a-repository).
- A repository token that reaches it, with **Read + write**. A **Read only** token can pull but not publish: CloudRepo answers `gradle publish` with `403`. See [Repository tokens](/docs/authenticate/repository-tokens.html) to create one. Your username is the email address of the account that created the token, and your password is the token.
- Your repository’s URL, `https://<organization>.mycloudrepo.io/repositories/<repository>`: your organization’s name, then the repository’s name as the admin portal shows it.

## Publish with gradle publish

**Gradle**

**1. Keep the credential in `~/.gradle/gradle.properties`,** outside your project, so it is never committed. The username is your email address, and the password is the token. Keep the file readable by you alone (`chmod 600 ~/.gradle/gradle.properties`), because it holds the token.

`~/.gradle/gradle.properties`

```properties
cloudrepoUsername=you@example.com
cloudrepoToken=YOUR_REPOSITORY_TOKEN
```

**2. Add a publication and the repository to your build script.** The `maven-publish` plugin must be applied, and your project needs a `group` and a `version`. The block reads the credential from the two properties above:

`build.gradle.kts`

```kotlin
publishing {
    publications {
        create<MavenPublication>("library") {
            from(components["java"])
        }
    }
    repositories {
        maven {
            url = uri("https://your-org.mycloudrepo.io/repositories/your-repo")
            credentials {
                username = providers.gradleProperty("cloudrepoUsername").get()
                password = providers.gradleProperty("cloudrepoToken").get()
            }
        }
    }
}
```

A Groovy build script takes the same block:

`build.gradle`

```groovy
publishing {
    publications {
        library(MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            url = "https://your-org.mycloudrepo.io/repositories/your-repo"
            credentials {
                username = findProperty("cloudrepoUsername")
                password = findProperty("cloudrepoToken")
            }
        }
    }
}
```

**3. Publish.** Run this in your project’s directory:

**Terminal**

```bash
gradle publish
```

Expected: `BUILD SUCCESSFUL`, with a `publish...PublicationToMavenRepository` task in the output. Gradle uploads the JAR, the POM and a Gradle module metadata file (`.module`), each with its checksum files, then `maven-metadata.xml`.

**4. Check that it landed.** Fetch the POM you published. Put the credential in `~/.netrc` and ask curl to read it, and keep the file readable by you alone (`chmod 600 ~/.netrc`).

`~/.netrc`

```ini
machine your-org.mycloudrepo.io
login you@example.com
password YOUR_REPOSITORY_TOKEN
```

**Terminal**

```bash
curl --netrc --fail --output docs-gradle-1.0.0.pom \
  https://your-org.mycloudrepo.io/repositories/your-repo/com/example/docs/docs-gradle/1.0.0/docs-gradle-1.0.0.pom
```

The path under the repository URL is your project’s `group` (dots become slashes), the artifact name (your project’s name), the version and the file. The artifact is also listed in the repository in the [admin portal](https://admin.cloudrepo.io).

## Publish from CI

Keep the token in your CI’s secret store and hand it to Gradle through the environment, so no file is written. Gradle turns an environment variable named `ORG_GRADLE_PROJECT_` plus a property name into that project property, so the same build script works unchanged:

**Terminal**

```bash
export ORG_GRADLE_PROJECT_cloudrepoUsername="you@example.com"
export ORG_GRADLE_PROJECT_cloudrepoToken="$CLOUDREPO_TOKEN"
gradle publish
```

## Snapshots and releases

Give a build in progress a version that ends in `-SNAPSHOT`, such as `version = "1.0.1-SNAPSHOT"`, and CloudRepo accepts it again on every publish. A release version, one that does not end in `-SNAPSHOT`, is published once: a second publish to the same version is refused with `409`, and the artifact already there stays as it is. See [Overwrite Protection](/docs/manage/repositories.html#overwrite-protection), the default setting behind this.

## When a publish is refused

Gradle prints the status it received, such as `Could not PUT '...'. Received status code 409 from server: Conflict`, and not the explanation CloudRepo sends with it. Check these in order:

- **401 Unauthorized.** The credential was refused. The username must be the email address of the account that created the token, and the password the token itself. A token that is expired or revoked fails the same way: the **Repository Tokens** page in the admin portal shows its status. In the Kotlin block, a property that Gradle cannot find fails before any request, with Gradle’s own message.
- **403 Forbidden.** The token cannot publish here. A **Read only** token cannot publish at all, and a token reaches only the repositories ticked when it was created. Use a **Read + write** token that includes this repository.
- **409 Conflict.** You published a release version that is already in the repository. Nothing is broken: CloudRepo refuses to replace a release on purpose. Publish a new version, or use a `-SNAPSHOT` version while you iterate. If the repository is meant to accept republished releases, turn **Overwrite Protection** off in its settings, knowing that a build that resolved that version yesterday can then get different bytes today.
- **413 Request Entity Too Large.** One file is over 50 GB (50,000,000,000 bytes), the most a single file can be.

More: [Repository tokens](/docs/authenticate/repository-tokens.html), for every client’s credential; [Publish Maven artifacts](/docs/publish/maven.html), if Maven builds your project.

---

The page: https://www.cloudrepo.io/docs/publish/gradle.html
