# Dependabot and Renovate with CloudRepo

> Point Dependabot and Renovate at your CloudRepo repositories for Maven, npm, PyPI and Docker, with the token kept as a secret and never written in the file.

Dependabot and Renovate look up new versions of the dependencies a project uses. To look one up in a private repository, a bot needs the repository’s address and a credential. This page gives the registry block for each bot and each of the four formats CloudRepo hosts, for a team that keeps either bot when it moves from Artifactory or Nexus.

CloudRepo’s documentation tests do not run Dependabot or Renovate, so the two blocks below are marked as not run. The two single-registry blocks further down are marked the same way. Check the first update on your own repository.

## Before you start

- A repository of each format you want the bot to read, and a [repository token](/docs/authenticate/repository-tokens.html) that reaches them. Pulling needs only a **Read only** token, so create one for the bot that reaches just the repositories it reads.
- Create the token as an account that outlives any one person, and give it an expiry that you replace it before. These are rules 1 and 2 on the [CI/CD overview](/docs/ci/overview.html#the-rule-every-guide-follows). Deleting a user deletes that user’s tokens, and a bot using one stops authenticating, like any other pipeline.
- Your repository URL, `https://your-org.mycloudrepo.io/repositories/your-repo`, and, for Docker, your host alone, `your-org.mycloudrepo.io`. Each block below names a repository of its own format, so `your-repo` is a different repository in each.
- The username that Maven, Python and Docker clients send is the email address of the account that created the token. npm sends the token alone.

## Dependabot

### Store the token as a Dependabot secret

1. In your GitHub repository, click **Settings**, then under **Security** click **Secrets and variables**, then **Dependabot**.
2. Click **New repository secret**. Name it `CLOUDREPO_TOKEN` and paste the token as the value.

This is the Dependabot list, not the Actions list that [Publish from GitHub Actions](/docs/ci/github-actions.html) uses. For one token shared by several repositories, GitHub also keeps Dependabot secrets for an organization; see [Configuring access to private registries for Dependabot](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/configure-access-to-private-registries#storing-credentials-for-dependabot-to-use).

When a Dependabot event triggers a workflow, GitHub makes only Dependabot secrets available to it, and not the Actions secrets. A CI job that pulls from CloudRepo on a Dependabot pull request therefore needs the token as a Dependabot secret too. GitHub’s advice is to store it as an Actions secret and a Dependabot secret with identical names, so the workflow names it once. See [Troubleshooting Dependabot on GitHub Actions](https://docs.github.com/en/code-security/reference/supply-chain-security/troubleshoot-dependabot/dependabot-on-actions#accessing-secrets).

### Add the registries to dependabot.yml

GitHub reads the file from `.github/dependabot.yml` on your default branch. Each registry has a name of your choosing, a `type`, a `url` and a credential, and each `updates` entry lists the registries it may use.

`.github/dependabot.yml`

```yaml
version: 2
registries:
  cloudrepo-maven:
    type: maven-repository
    url: https://your-org.mycloudrepo.io/repositories/your-repo
    username: you@example.com
    password: ${{secrets.CLOUDREPO_TOKEN}}
  cloudrepo-npm:
    type: npm-registry
    url: https://your-org.mycloudrepo.io/repositories/your-repo/
    scope: "@acme"
    token: ${{secrets.CLOUDREPO_TOKEN}}
  cloudrepo-python:
    type: python-index
    url: https://your-org.mycloudrepo.io/repositories/your-repo/simple/
    username: you@example.com
    password: ${{secrets.CLOUDREPO_TOKEN}}
  cloudrepo-docker:
    type: docker-registry
    url: https://your-org.mycloudrepo.io
    username: you@example.com
    password: ${{secrets.CLOUDREPO_TOKEN}}
updates:
  - package-ecosystem: maven
    directory: "/"
    registries:
      - cloudrepo-maven
    schedule:
      interval: weekly
  - package-ecosystem: npm
    directory: "/"
    registries:
      - cloudrepo-npm
    schedule:
      interval: weekly
  - package-ecosystem: pip
    directory: "/"
    registries:
      - cloudrepo-python
    schedule:
      interval: weekly
  - package-ecosystem: docker
    directory: "/"
    registries:
      - cloudrepo-docker
    schedule:
      interval: weekly
```

Keep the entries for the formats you use and delete the rest. Replace `@acme` with the scope of your private npm packages, and `you@example.com` with the email address of the account that created the token. No block holds the token: `${{secrets.CLOUDREPO_TOKEN}}` is replaced by the Dependabot secret when Dependabot runs.

- **Maven.** The URL is the one in [Pull Maven artifacts](/docs/consume/maven.html), with the email address and the token as the username and password. For a Gradle project, GitHub’s own example pairs a `maven-repository` registry with `package-ecosystem: "gradle"`, so list `cloudrepo-maven` under a `gradle` update; the Gradle URL is the same, as [Pull Gradle dependencies](/docs/consume/gradle.html) shows.
- **npm.** The token goes in `token`, alone, with no username or password: that is what CloudRepo’s npm door takes, as [Install npm packages](/docs/consume/npm.html#point-npm-at-the-repository) shows. GitHub says Dependabot does not set environment variables, so give it the token through this entry and not through the `${CLOUDREPO_TOKEN}` of a committed `.npmrc`. GitHub also says that with a `scope`, Dependabot builds the `.npmrc` from the entry’s credentials and uses it in place of a committed one. The `scope` is the scope of the `@acme:registry=` line on the npm page. If your `.npmrc` sets `registry=` for every package, use `replaces-base: true` in place of `scope`; GitHub says either one makes Dependabot build the `.npmrc` from the entry’s credentials.
- **Python.** The username is the email address and the password is the token, the same login pip reads from the `login` and `password` of its `~/.netrc` entry in [Install Python packages](/docs/consume/python.html#point-pip-at-the-repository), and as in [Publish Python packages](/docs/publish/python.html). The entry keeps them in `username` and `password`, so the URL carries no credential, as the index URL on the Python page carries none. GitHub lets a `python-index` entry carry a `token` instead; this entry uses the username and password that the Python pages document, because CloudRepo refuses a username that is not the token’s email address, `__token__` included. The URL is the index URL on the Python page.
- **Docker.** The URL is the host alone, as in [Pull Docker images](/docs/consume/docker.html#log-in). The image names in your Dockerfile carry the repository after the host, as that page shows.
- **`replaces-base`.** The entries above leave it out, because each points at a hosted repository, which holds only what was published to it. GitHub says `replaces-base: true` makes Dependabot resolve dependencies from the entry’s `url` and not from the ecosystem’s own registry. Leaving it out costs something for Maven and Python, and [the next section](#keep-a-public-package-from-standing-in-for-a-private-one) says what, and how to avoid it. The npm entry carries a `scope`, which is a different case.
- **One block per path.** GitHub says the `maven-repository`, `python-index` and `docker-registry` types match the path in `url` as a prefix, so an entry sends its credential for that path only. Two Maven repositories on your host are two entries.

### Keep a public package from standing in for a private one

GitHub says Dependabot can access public registries by default, and that adding a registry lets it reach a private one as well; see [Removing Dependabot access to public registries](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/remove-access-to-public-registries). So without `replaces-base`, a Maven or Python entry above is one more place for Dependabot to look, and the public registry of the ecosystem, Maven Central for Maven and PyPI for Python, stays one of the places. GitHub does not say which registry wins when a name is in both. If a private Maven group and artifact, or a private Python package, has the name of a public one, or is given it later, Dependabot can find the public package where you expect your own. That is dependency confusion. [Install Python packages](/docs/consume/python.html#install-private-and-public-packages-together) gives the same warning for pip: “Anyone can publish a package on PyPI under your private package’s name, and an install that can read PyPI as well may install theirs instead.”

With `replaces-base: true`, GitHub says Dependabot resolves dependencies using the entry’s `url` rather than the base URL of that ecosystem; see [`url` and `replaces-base`](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#url-and-replaces-base). GitHub’s page on removing access to public registries lists it as a way for Maven, and for pip, to reach only the private registry. Every lookup then goes where `url` points, so the `url` has to hold your public dependencies too. A hosted repository does not.

**Maven: use a group.** A [Maven group repository](/docs/formats/maven.html#maven-group-repositories-nexus-maven-public-artifactory-virtual) is one address in front of several repositories. Create one, add your hosted repository and a [proxy repository](/docs/consume/proxy-repositories.html) of Maven Central as its members, and give the hosted repository the lowest **Priority** number. The group tries its members in priority order and the first member that holds a file serves it, so a file your hosted repository holds is served from it. Then point the entry at the group, with `replaces-base: true`:

`.github/dependabot.yml (Maven through a group)`

```yaml
version: 2
registries:
  cloudrepo-maven-group:
    type: maven-repository
    url: https://your-org.mycloudrepo.io/repositories/maven-public
    username: you@example.com
    password: ${{secrets.CLOUDREPO_TOKEN}}
    replaces-base: true
updates:
  - package-ecosystem: maven
    directory: "/"
    registries:
      - cloudrepo-maven-group
    schedule:
      interval: weekly
```

Replace `maven-public` with the name of your group, and `you@example.com` with the email address of the account that created the token. A group with a proxy member, as this one has, serves `maven-metadata.xml` from the first member that holds one and does not merge the members’ files; read [what a group does with `maven-metadata.xml`](/docs/formats/maven.html#maven-group-repositories-nexus-maven-public-artifactory-virtual) before you rely on it.

This block is for `package-ecosystem: maven`. For a `gradle` update GitHub’s page says the opposite: leave `replaces-base` out, and name the private registry URL in the `repositories` block of `build.gradle`; see [its Gradle section](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/remove-access-to-public-registries#gradle). Name the group’s URL there. [Keep your own groups off other repositories](/docs/consume/gradle.html#keep-your-own-groups-off-other-repositories) shows Gradle’s own way to hold a group of yours to CloudRepo.

**Python: choose one registry.** CloudRepo has no group for Python: a group is offered for Maven and Docker only, as [Creating a Repository](/docs/manage/repositories.html#creating-a-repository) says. A Python repository either holds what you published and no public package, or is a proxy of PyPI and holds no private package of yours. No CloudRepo address serves both, so for each `updates` entry you choose which one Dependabot reads:

- Set `replaces-base: true` on the entry for your hosted repository, for a directory whose requirements are all private packages. Dependabot then reads that repository and not PyPI, which GitHub lists as an option for pip. A requirement on a public package is not found there.
- Or leave `replaces-base` out, and Dependabot reads PyPI as well, with the risk above for every private package name you have.

GitHub’s page treats pip, pip-compile, Pipenv and Poetry one by one, and for Pipenv it says to remove `replaces-base`; read [its Python section](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/remove-access-to-public-registries#python) for your manager before you copy a setting.

**npm.** The npm entry above carries a `scope`. GitHub says a `scope` associates the npm scope with the registry, and npm sends the packages of a scope to the registry that scope names, as [Install npm packages](/docs/consume/npm.html#point-npm-at-the-repository) shows. What this section describes is about the Maven and Python entries.

## Renovate

### Store the token as a Renovate secret

On the Mend Renovate App, add the token as a secret named `CLOUDREPO_TOKEN` in the **Credentials** section of your organization’s or repository’s settings on [developer.mend.io](https://developer.mend.io). You need admin rights there to add a secret. If you run Renovate yourself, the administrator defines the secret in the `secrets` option of the bot’s own configuration.

Either way, a repository’s `renovate.json` names the secret as `{{ secrets.CLOUDREPO_TOKEN }}`, and Renovate puts the value in when it runs.

### Add hostRules to renovate.json

`renovate.json`

```json
{
  "hostRules": [
    {
      "hostType": "maven",
      "matchHost": "https://your-org.mycloudrepo.io/repositories/your-repo/",
      "username": "you@example.com",
      "password": "{{ secrets.CLOUDREPO_TOKEN }}"
    },
    {
      "hostType": "npm",
      "matchHost": "https://your-org.mycloudrepo.io/repositories/your-repo/",
      "token": "{{ secrets.CLOUDREPO_TOKEN }}"
    },
    {
      "hostType": "pypi",
      "matchHost": "https://your-org.mycloudrepo.io/repositories/your-repo/",
      "username": "you@example.com",
      "password": "{{ secrets.CLOUDREPO_TOKEN }}"
    },
    {
      "hostType": "docker",
      "matchHost": "your-org.mycloudrepo.io",
      "username": "you@example.com",
      "password": "{{ secrets.CLOUDREPO_TOKEN }}"
    }
  ]
}
```

Keep the rules for the formats you use and delete the rest. Replace `you@example.com` with the email address of the account that created the token. No rule holds the token.

- **Maven, Python and Docker** take the email address as the `username` and the token as the `password`, the same credential as [Pull Maven artifacts](/docs/consume/maven.html), [Install Python packages](/docs/consume/python.html) and [Pull Docker images](/docs/consume/docker.html).
- **npm** takes the token alone, in `token`, with no username, password or `authType`. Renovate sends a `token` as a Bearer token unless you set `authType`, and CloudRepo’s npm door takes the token as a Bearer, as [Install npm packages](/docs/consume/npm.html#point-npm-at-the-repository) shows. Keep `matchHost` identical to the registry URL in your `.npmrc`, with its trailing slash.
- **`matchHost`.** Renovate matches a URL that ends in `/` against that path and everything below it, and a bare host name against that host and its subdomains. The Maven, npm and Python rules name the repository’s path, so each applies to that repository only. The Docker rule names the host, because an image name carries its repository after the host.
- **Where Renovate looks.** `hostRules` supply credentials; they do not say which registry to read. Renovate usually detects a custom registry from your project’s files, and when it cannot, you name it with `registryUrls` in a `packageRules` entry, as Renovate’s documentation shows. For Maven and Python, see [Make Renovate read one registry](#make-renovate-read-one-registry).

### Make Renovate read one registry

The `hostRules` above supply a credential and do not choose a registry. Renovate’s documentation says a datasource’s default registries are used only when the manager extracted no `registryUrls` and none were set in config, and calls `registryUrls` a way to override them; see [`defaultRegistryUrls`](https://docs.renovatebot.com/configuration-options/#defaultregistryurls) and [`registryUrls`](https://docs.renovatebot.com/configuration-options/#registryurls). Its [Java page](https://docs.renovatebot.com/java/#custom-registry-support-and-authentication) shows a Maven lookup overridden by a `packageRules` entry that sets `registryUrls`, and its [Python page](https://docs.renovatebot.com/python/#alternate-registries) says Renovate checks `pypi.org` by default. So, as for Dependabot, name the one registry you want Renovate to read. For Maven that is the group described above, with a `hostRules` entry that reaches it:

`renovate.json (Maven through a group)`

```json
{
  "hostRules": [
    {
      "hostType": "maven",
      "matchHost": "https://your-org.mycloudrepo.io/repositories/maven-public/",
      "username": "you@example.com",
      "password": "{{ secrets.CLOUDREPO_TOKEN }}"
    }
  ],
  "packageRules": [
    {
      "matchDatasources": ["maven"],
      "registryUrls": ["https://your-org.mycloudrepo.io/repositories/maven-public"]
    }
  ]
}
```

Replace `maven-public` with the name of your group, and `you@example.com` with the email address of the account that created the token. Name one URL: Renovate says it depends on the datasource whether only the first of several `registryUrls` is used or all of them.

For Python, CloudRepo has no group, so the choice described for Dependabot is yours here too: one index in `registryUrls`, your hosted repository for private packages or a proxy of PyPI for public ones. Renovate’s Python page also says that an `index-url` in a `requirements.txt` is followed instead of a `registryUrls` set in config, so check that file.

## When a bot cannot read a repository

A `401`, `403` or `404` from CloudRepo has the same causes for a bot as for any client: see “When a Client Answers 401, 403 or 404” on [Repository tokens](/docs/authenticate/repository-tokens.html). CloudRepo’s npm door answers a Bearer token that is mistyped, expired, revoked or from another organization with `404 Not Found`, not `401`, so a bot that reports an npm package as not found when you know it exists should send you to the token first, as [Install npm packages](/docs/consume/npm.html#when-npm-answers-401-403-or-404) explains.

## More

[Repository tokens](/docs/authenticate/repository-tokens.html), for every client’s credential; [CI/CD overview](/docs/ci/overview.html), for where each platform keeps a secret; [Migrate from JFrog Artifactory](/docs/migrate/from-jfrog.html), to repoint your builds before you repoint the bots; GitHub’s [Dependabot options reference](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference) and Renovate’s [hostRules](https://docs.renovatebot.com/configuration-options/#hostrules), for every other option.

---

The page: https://www.cloudrepo.io/docs/ci/dependabot-and-renovate.html
