Basic Configuration Examples

This guide provides configuration examples for common package managers and build tools to get you started with CloudRepo quickly.

Maven Configuration

Settings File (~/.m2/settings.xml)

Maven settings.xml - Basic CloudRepo Configuration
<?xml version="1.0" encoding="UTF-8"?>
<settings>
  <servers>
    <server>
      <id>cloudrepo</id>
      <username>your-username</username>
      <password>$YOUR_API_TOKEN</password>
    </server>
  </servers>

  <profiles>
    <profile>
      <id>cloudrepo</id>
      <repositories>
        <repository>
          <id>cloudrepo</id>
          <url>https://[org-id].mycloudrepo.io/repositories/maven-releases</url>
        </repository>
      </repositories>
    </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>cloudrepo</activeProfile>
  </activeProfiles>
</settings>

Project POM Configuration

pom.xml - Distribution Management
<distributionManagement>
  <repository>
    <id>cloudrepo</id>
    <url>https://[org-id].mycloudrepo.io/repositories/maven-releases</url>
  </repository>
  <snapshotRepository>
    <id>cloudrepo</id>
    <url>https://[org-id].mycloudrepo.io/repositories/maven-snapshots</url>
  </snapshotRepository>
</distributionManagement>

Gradle Configuration

Build Script (build.gradle)

build.gradle - Groovy DSL Configuration
repositories {
    maven {
        url "https://[org-id].mycloudrepo.io/repositories/maven-releases"
        credentials {
            username = project.findProperty("cloudrepoUsername") ?: System.getenv("CLOUDREPO_USERNAME")
            password = project.findProperty("cloudrepoPassword") ?: System.getenv("CLOUDREPO_API_TOKEN")
        }
    }
}

publishing {
    publications {
        maven(MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            url = "https://[org-id].mycloudrepo.io/repositories/maven-releases"
            credentials {
                username = cloudrepoUsername
                password = cloudrepoPassword
            }
        }
    }
}

Kotlin DSL (build.gradle.kts)

build.gradle.kts - Kotlin DSL Configuration
repositories {
    maven {
        url = uri("https://[org-id].mycloudrepo.io/repositories/maven-releases")
        credentials {
            username = findProperty("cloudrepoUsername")?.toString() ?: System.getenv("CLOUDREPO_USERNAME")
            password = findProperty("cloudrepoPassword")?.toString() ?: System.getenv("CLOUDREPO_API_TOKEN")
        }
    }
}

Python Configuration

Pip Configuration (~/.pip/pip.conf)

pip.conf - Global Pip Configuration
[global]
index-url = https://member-email%40your-org.com:YOUR_API_TOKEN@[org-id].mycloudrepo.io/repositories/pypi/simple

Your CloudRepo username is an email address. Percent-encode the @ inside it as %40: it is the form RFC 3986 specifies, and not every tool that reads this URL is as forgiving as pip about a second literal @.

Warning

Do not set trusted-host for a CloudRepo host. pip documents --trusted-host as marking a host trusted “even though it does not have valid or any HTTPS”. It turns off certificate verification for that host, so the credential in the line above would travel over a connection that accepts any certificate.

It buys you nothing here: CloudRepo serves *.mycloudrepo.io (the host in the line above) with a publicly trusted certificate that validates with no extra configuration. On a custom domain, trust the CA that issued the certificate you configured.

Note

Behind a TLS-inspecting corporate proxy? If your network presents its own certificate authority, point pip at that CA rather than switching verification off:

# Try this FIRST, before setting anything: if the machine already trusts your
# corporate CA at the OS level, pip 24.2+ on Python 3.10+ reads the OS trust
# store on its own. Often no `cert` setting is needed at all.

# If you do need to name a file, use YOUR distribution's bundle -- the path is
# not portable, and pip fails hard ("Could not find a suitable TLS CA certificate
# bundle") when it does not exist:
#   Debian / Ubuntu     /etc/ssl/certs/ca-certificates.crt
#   RHEL / Rocky / Alma /etc/pki/tls/certs/ca-bundle.crt
#   macOS / Windows     no equivalent -- build a bundle instead
pip config set global.cert /etc/ssl/certs/ca-certificates.crt

# Otherwise build a bundle yourself. Note the bootstrap: installing certifi needs
# a working TLS path to PyPI, which is the thing currently broken.
pip install certifi
CERTIFI_PEM="$(python3 -m certifi)" && \
  cat "$CERTIFI_PEM" /path/to/corporate-ca.crt > /path/to/corporate-bundle.pem
pip config set global.cert /path/to/corporate-bundle.pem

The && is load-bearing: without it, a missing certifi would still let > truncate the file, leaving you pinned to a bundle holding only the corporate CA.

Why a bundle rather than the bare corporate CA? Because on many machines cert replaces the trust store outright, and a corporate-CA-only file then breaks every install from pypi.org.

Whether it replaces depends on Python’s version, not pip’s: pip enables its truststore backend only on Python 3.10+. So on Python 3.9 or older – which includes the system Python on RHEL, Rocky and Alma 9 – cert still replaces, however new your pip is. And pip 24.1 and earlier replace regardless of Python – pip enabled truststore by default at 24.2.

A concatenated bundle is correct in every combination, which is why it is the form given here. (pip’s --cert help still reads “If provided, overrides the default”.)

pip also honors PIP_CERT, REQUESTS_CA_BUNDLE and CURL_CA_BUNDLE for the same purpose. All three beat pip.conf. Measured precedence, strongest first:

REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE   >   PIP_CERT   >   pip.conf global.cert

So if an install still fails after you have set global.cert, check the environment for all three before changing anything else, starting with REQUESTS_CA_BUNDLE, because it wins over the other two.

Supplying the Token from Your Keyring

The index-url form above is the standard pip configuration and it works. It does store your API token in plain text in pip.conf, and any command that echoes the index URL will print it. On a shared or long-lived machine you may prefer to have pip read the token from your system keyring instead:

pip install keyring
keyring set [org-id].mycloudrepo.io member-email@your-org.com

With the token in the keyring, leave it out of the URL:

pip.conf - credential supplied by keyring
[global]
index-url = https://member-email%40your-org.com@[org-id].mycloudrepo.io/repositories/pypi/simple

Your CloudRepo username is an email address, so percent-encode the @ inside it as %40 when it appears in a URL. pip itself parses the raw form correctly (it splits the userinfo on the last @) and unquotes %40 back to a plain @, so either spelling matches the username you gave keyring set. Prefer %40 anyway: it is the form RFC 3986 specifies, and the other tools that read this same URL are not all as forgiving.

Select the keyring mechanism with --keyring-provider (auto, disabled, import, subprocess; the default is auto). The subprocess provider requires the username to be present in the URL, as shown above.

Two things that make keyring look broken when it is not. pip only consults the keyring after an unauthenticated request comes back 401, so the first request on each run is expected to fail. And the auto provider does not query the keyring at all under --no-input / PIP_NO_INPUT=1, which is common in CI. Pass --keyring-provider import there. Use import rather than subprocess: pip looks for the keyring CLI on PATH with its own scripts directory removed, so a keyring installed by the pip install keyring above is exactly where pip refuses to look, and subprocess silently falls back to disabled and sends no credential. On a headless machine with no Secret Service backend, keyring may have no secure store to use at all; check keyring --list-backends before relying on it.

Twine Configuration (~/.pypirc)

.pypirc - Publishing Configuration
[distutils]
index-servers =
    cloudrepo

[cloudrepo]
repository = https://[org-id].mycloudrepo.io/repositories/pypi
username = member-email@your-org.com
password = YOUR_API_TOKEN

.pypirc is read literally: it performs no shell expansion, so paste the token itself rather than a $VARIABLE reference. Unlike the index-url form, the username here is not part of a URL, so the @ needs no encoding.

Twine can also take the token from your keyring, which keeps it out of .pypirc altogether:

keyring set https://[org-id].mycloudrepo.io/repositories/pypi member-email@your-org.com

In CI, where writing a .pypirc is awkward, twine also accepts the TWINE_USERNAME and TWINE_PASSWORD environment variables.

Poetry Configuration

pyproject.toml - Poetry Configuration
[[tool.poetry.source]]
name = "cloudrepo"
url = "https://[org-id].mycloudrepo.io/repositories/pypi/simple"
priority = "primary"

[tool.poetry.repositories.cloudrepo]
url = "https://[org-id].mycloudrepo.io/repositories/pypi"

npm Configuration

.npmrc Configuration

.npmrc - npm Registry Configuration
registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
//[org-id].mycloudrepo.io/repositories/[repo-name]/:_authToken=YOUR_NPM_TOKEN

Mint the token with the npm CLI against your repository registry. Tokens are issued by the registry, not by the admin portal:

npm token create --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/

Authenticate as a dedicated repository user (created in the admin portal under Users → Create a Repository User) rather than as the organization owner, and grant that user only the repositories the workload needs.

For CI/CD with base64-encoded auth:

.npmrc - CI/CD Configuration
registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
//[org-id].mycloudrepo.io/repositories/[repo-name]/:_auth=BASE64_ENCODED

Note

Modern npm (v10+) rejects email addresses as usernames, so the interactive npm login command does not work for CloudRepo customers. Use the .npmrc token pattern above instead. (We’re working on a web login flow that will let npm login open the admin portal in a browser. Coming soon.)

Environment Variables

Secure Credential Management

Instead of hardcoding credentials, use environment variables. CI/CD workloads should use API tokens, never passwords:

.env or Shell Configuration
export CLOUDREPO_USERNAME="member-email@your-org.com"
export CLOUDREPO_API_TOKEN="generated-from-admin-portal"
export CLOUDREPO_ORG="your-org-id"

API tokens are repo-scoped, revocable, and bound to a member identity. That makes them safer than portal passwords for any automated workload. Org owners cannot authenticate at package surfaces with their portal password; they must mint a token bound to a member user.

CI/CD Basic Setup

GitHub Actions

.github/workflows/deploy.yml - Basic GitHub Actions
- name: Setup CloudRepo
  env:
    CLOUDREPO_USERNAME: ${{ secrets.CLOUDREPO_USERNAME }}
    CLOUDREPO_API_TOKEN: ${{ secrets.CLOUDREPO_API_TOKEN }}
  run: |
    echo "<settings>...</settings>" > ~/.m2/settings.xml

Jenkins

Jenkinsfile - Basic Jenkins Pipeline
pipeline {
    environment {
        CLOUDREPO_CREDS = credentials('cloudrepo-credentials')
    }
    stages {
        stage('Deploy') {
            steps {
                sh 'mvn deploy -DcloudrepoUsername=$CLOUDREPO_CREDS_USR -DcloudrepoPassword=$CLOUDREPO_CREDS_PSW'
            }
        }
    }
}

Testing Your Configuration

Verify Connection

Maven:

mvn dependency:get -DremoteRepositories=https://[org-id].mycloudrepo.io/repositories/maven-releases \
                   -DgroupId=com.example -DartifactId=test -Dversion=1.0

Python:

pip install --index-url https://[org-id].mycloudrepo.io/repositories/pypi/simple --dry-run your-package

npm:

# Verify authentication
npm whoami --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/

# Verify registry connectivity
npm ping --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/

Common Configuration Issues

Authentication Errors

  • 401 Unauthorized: Check username/password

  • 403 Forbidden: Verify repository permissions

  • Certificate Errors: Ensure proper HTTPS configuration

URL Format Issues

  • Maven: Ensure URL ends with repository name, not /

  • Python: Include /simple for pip index URLs

Proxy Configuration

If behind corporate proxy:

export HTTP_PROXY=http://proxy.company.com:8080
export HTTPS_PROXY=http://proxy.company.com:8080
export NO_PROXY=[org-id].mycloudrepo.io

Next Steps