Skip to content

Change every Nexus and Artifactory URL in your builds, lockfiles included

View as Markdown

A build names its repository in more places than one settings file, and a lockfile names it again. package-lock.json and Yarn 1’s yarn.lock hold the tarball URL of each package, and poetry.lock and uv.lock hold the URL of the index each package came from. If you change the settings and leave the lockfiles, npm ci goes on downloading from Nexus or Artifactory until the day you switch it off, and Poetry and uv refuse a lockfile that no longer matches your settings.

This page maps each Nexus 2, Nexus 3 and Artifactory URL to its CloudRepo URL, gives one search that finds every one in your repositories, and gives the step that regenerates each lockfile. It changes URLs. It does not copy files: to copy them, see Migrate from Sonatype Nexus Repository or Migrate from JFrog Artifactory.

Nexus Repository 3 writes a repository’s URL with the repository’s name after /repository/, for Maven, npm and Python. Nexus Repository 2 writes a hosted repository’s URL with its name after /nexus/content/repositories/ and a repository group’s after /nexus/content/groups/, for Maven and npm, as the PDF linked from Sonatype’s Nexus Repository 2 help shows. Each row shows a URL as Sonatype’s examples write it, with nexus.example for your host and the repository names the examples use, and the same repository on CloudRepo. The first three rows are Nexus 3 and the last four are Nexus 2, which their first cell says:

Build tools URL in Nexus URL on CloudRepo
Maven and Gradle https://nexus.example/repository/maven-public/ https://your-org.mycloudrepo.io/repositories/your-repo
npm and Yarn https://nexus.example/repository/npm-all/ https://your-org.mycloudrepo.io/repositories/your-repo/
pip, Poetry and uv https://nexus.example/repository/pypi-all/simple https://your-org.mycloudrepo.io/repositories/your-repo/simple/
Maven and Gradle, Nexus 2 group https://nexus.example/nexus/content/groups/public https://your-org.mycloudrepo.io/repositories/your-repo
Maven and Gradle, Nexus 2 repository https://nexus.example/nexus/content/repositories/releases/ https://your-org.mycloudrepo.io/repositories/your-repo
npm and Yarn, Nexus 2 group https://nexus.example/nexus/content/groups/npm-all/ https://your-org.mycloudrepo.io/repositories/your-repo/
npm and Yarn, Nexus 2 repository https://nexus.example/nexus/content/repositories/npm-internal/ https://your-org.mycloudrepo.io/repositories/your-repo/

The /nexus in a Nexus 2 URL is the server’s webapp context path. Sonatype’s help says it is /nexus by default and shows how to move it to the root, so your URLs may have another path, or none, before /content/. The search below finds a Nexus 2 URL either way.

JFrog writes a repository’s URL with the repository key after /artifactory/, and after /artifactory/api/npm/ or /artifactory/api/pypi/ for npm and Python, as its pages for Maven, npm and Python show. Each row shows a URL as JFrog’s examples write it, with acme.jfrog.io for your host, and the same repository on CloudRepo:

Build tools URL in Artifactory URL on CloudRepo
Maven and Gradle https://acme.jfrog.io/artifactory/libs-release https://your-org.mycloudrepo.io/repositories/your-repo
npm and Yarn https://acme.jfrog.io/artifactory/api/npm/npm-virtual/ https://your-org.mycloudrepo.io/repositories/your-repo/
pip, Poetry and uv https://acme.jfrog.io/artifactory/api/pypi/pypi-virtual/simple https://your-org.mycloudrepo.io/repositories/your-repo/simple/

Copy the CloudRepo column as it is written. The Maven URL has no trailing slash, the npm URL ends in / and the Python index URL ends in /simple/, as Pull Maven artifacts, Install npm packages and Install Python packages show. The last part of each is the name of the CloudRepo repository you moved those packages to, so before you search, write down each Nexus or Artifactory repository your builds use and the CloudRepo repository that replaces it.

A Nexus group or an Artifactory virtual repository can serve your own packages and public ones from one URL. CloudRepo has group repositories for Maven and Docker and none for npm or Python: Artifactory and Nexus terms in CloudRepo says what takes their place.

This command lists every line, in the files below that Git tracks, that names an old host or holds /repository/ (Nexus 3), /content/repositories/ or /content/groups/ (Nexus 2), or /artifactory/ (Artifactory) in a URL, so a host you did not think of shows up too. Run it in each repository you have, and write your own host names in place of nexus.example and acme.jfrog.io, with a backslash before each dot:

Terminal
git grep -n -i -E '/repository/|/artifactory/|/content/repositories/|/content/groups/|nexus\.example|acme\.jfrog\.io' -- '*pom.xml' '*settings.xml' '*build.gradle' '*build.gradle.kts' '*settings.gradle' '*settings.gradle.kts' '*.npmrc' '*package-lock.json' '*yarn.lock' '*.yarnrc.yml' '*pip.conf' '*requirements*.txt' '*pyproject.toml' '*poetry.lock' '*uv.lock'

Each hit is a file and a line. A hit in package-lock.json, yarn.lock, poetry.lock or uv.lock is a lockfile, and the next section regenerates it. A hit in any other file is a URL to change by the tables above, unless it names a public server that is not yours: leave that URL as it is. The list includes pyproject.toml, which holds the index of Poetry and uv, and .yarnrc.yml, which holds the registry of Yarn 2 and later.

Change the settings first, so that every tool below already points at CloudRepo. Then regenerate each lockfile with the tool that wrote it, so the tool writes the new URLs itself. Afterwards read git diff of the lockfile: it should change URLs, and it shows any version that moved.

Each package in package-lock.json has a resolved field. npm describes it in its lockfile page as “The place where the package was actually resolved from”, a tarball URL for a package that comes from a registry. By default npm replaces the host of a resolved URL only when it is https://registry.npmjs.org (replace-registry-host), so a resolved URL that names Nexus or Artifactory is used as it is written, and npm ci goes on downloading from there when your .npmrc already names CloudRepo.

Remove node_modules as well as the lockfile. npm keeps a hidden lockfile in node_modules, and with that folder in place npm install reads the old URLs back from it and writes them into the new lockfile.

Terminal
rm -rf node_modules package-lock.json
npm install

With no lockfile, npm install resolves each dependency from the ranges in package.json, which npm calls “the source of truth for acceptable version ranges”, so the new lockfile can hold newer versions than the old one. Check the diff, run your tests, and end with npm ci. If you must keep every locked version, replace the old URL with the CloudRepo URL in each resolved line yourself and run npm ci.

Yarn 1 writes resolved "<URL>#<hash>" for each package, as the example on its lockfile page shows, and that page says the file is “auto-generated and should be handled entirely by Yarn”. With a yarn.lock that is enough for package.json, Yarn’s install page says “the exact versions recorded in yarn.lock are installed, and yarn.lock will be unchanged”, so Yarn does not rewrite the URLs. Without one, Yarn “looks for the newest versions available that satisfy the constraints in package.json” and writes them to a new yarn.lock.

Terminal
rm yarn.lock
yarn install

The new file can hold newer versions than the old one, so check the diff and run your tests.

Yarn 2 and later read the registry from npmRegistryServer in .yarnrc.yml (Yarn configuration). A yarn.lock written by Yarn 4 lists a package from the registry by its resolution and checksum and holds no registry URL, so there is no URL to change in it. Set npmRegistryServer, run yarn install, and run the search again. If the search still finds the old host in yarn.lock, delete the file and run yarn install as above.

For each package that comes from a source you named in pyproject.toml, poetry.lock holds a [package.source] table with the source’s url. Set the source as Poetry Repositories shows, then lock again. poetry install stops with “pyproject.toml changed significantly since poetry.lock was last generated” until you do, and poetry lock writes the new url. Poetry’s lock command says “packages that have already been added to the lock file before will not be updated”, so the versions stay.

Terminal
poetry lock

If the search still finds the old host in poetry.lock, run poetry lock --regenerate. Poetry describes that option as “Ignore existing lock file and overwrite it with a new lock file created from scratch”, and says it and poetry update --lock “normally produce the same result”, which moves each dependency to its latest compatible version. Check the diff.

uv.lock holds source = { registry = "<index URL>" } for each package that comes from an index, and the URL of each of its files. uv says the file “is managed by uv and should not be edited manually”. Set the index as uv Repositories shows, then lock again. With the index changed, uv lock --check and uv sync --locked stop with “The lockfile at uv.lock needs to be updated”, and uv lock writes the new URLs. uv “will prefer the previously locked versions of packages”, so the versions stay unless your constraints exclude them.

Terminal
uv lock

The search reads the files that Git tracks, in the repositories you ran it in. Check these by hand:

Run the search again: no file it lists should name Nexus or Artifactory. Then install from each lockfile with the command that never rewrites it, in a CI job or on a machine that cannot reach Nexus or Artifactory, so a URL you missed fails now and not on the day you switch it off:

Terminal
# npm
npm ci
# Yarn 1
yarn install --frozen-lockfile
# Yarn 2 and later
yarn install --immutable
# Poetry
poetry install
# uv
uv sync --locked

For Maven, Gradle and pip, run the build. Then follow step 6 of Migrate from Sonatype Nexus Repository or Migrate from JFrog Artifactory, which sets the order of the switch.