Move your projects by hand
After the copy from Nexus, each project still names Nexus. You move one project in two stages:
- Downloads. The build downloads from Telesphorepo. Publishing stays on Nexus.
- Publishing. The project publishes to Telesphorepo, first to a test repository.
You can do the first stage while the copy syncs. To let a coding agent do it with you, see Move your projects with an AI agent.
- Gradle or Maven
- npm, pnpm, Yarn or Bun
- Python
- Rust and Cargo
- .NET and NuGet
- Docker and Helm
- Debian or raw files
Before you start
Section titled “Before you start”Write down the name of each repository in Nexus and in Telesphorepo. The Repositories page lists them. A lock after a name means the repository is private.
The examples use https://repo.example.com for Telesphorepo and https://nexus.example.com for Nexus.
Find every Nexus address
Section titled “Find every Nexus address”Search every file of the project, hidden folders too, for two things:
- the Nexus host name
/repository/, which every Nexus address holds
Look in the files below. Also look in CI files, Dockerfiles, compose files, scripts and READMEs.
Gradle or Maven: where to look
pom.xml,.mvn/maven.configand everysettings.xmlbuild.gradle(.kts),settings.gradle(.kts),gradle.properties,gradle/*.gradle(.kts)buildSrc/,build-logic/and init scriptsgradle/wrapper/gradle-wrapper.properties- On your computer:
~/.m2/settings.xml,~/.gradle/gradle.propertiesand~/.gradle/init.d/
npm, pnpm, Yarn or Bun: where to look
.npmrc, in the project and in~/.npmrc.yarnrc.yml,.yarnrcandbunfig.tomlpackage.jsonand the lockfilesregistry-urlofactions/setup-nodein CI
Python: where to look
pip.conforpip.ini, in the project,~/.config/pip/and/etc/requirements*.txtand constraints filespyproject.toml,uv.toml,poetry.toml,setup.cfg,tox.ini,noxfile.pyPIP_INDEX_URL,PIP_EXTRA_INDEX_URL,UV_INDEX_URLandUV_DEFAULT_INDEXin Dockerfiles and CI.pypirc, for publishing
Rust and Cargo: where to look
.cargo/config.toml, in the project and in~/.cargo/Cargo.tomlandCargo.lock
.NET and NuGet: where to look
nuget.config, in the solution folder, its parents and your user folderDirectory.Build.propsand.targets*.csprojand*.fsprojdotnet nuget add source,--sourceandsource-urlofactions/setup-dotnetin CI
Docker and Helm: where to look
Also search for each Nexus Docker host and port, such as nexus.example.com:8082.
Dockerfile*and compose files- Kubernetes manifests and Kustomize
Chart.yaml,Chart.lockandvalues*.yaml- helmfile, skaffold, Tiltfile and devcontainer files
docker login,docker pull,helm registry loginandhelm repo addin CI
Debian or raw files: where to look
- apt sources, and the Dockerfiles, Ansible, cloud-init files and scripts that write them
- keyrings
curlandwgetof Nexus files
Change the download addresses
Section titled “Change the download addresses”Change each download address to Telesphorepo. Leave the publish addresses and publish steps on Nexus for now.
The files are the same bytes as in Nexus. Keep every checksum and hash.
Gradle or Maven: the addresses
https://nexus.example.com/repository/<nexus name>/ becomes:
https://repo.example.com/maven/<name>- Only the prefix changes. The files keep their paths.
- Point a
<mirror>with<mirrorOf>*</mirrorOf>at a Telesphorepo group. - A
distributionUrlon a Nexus raw repository becomeshttps://repo.example.com/raw/<name>/<path>. KeepdistributionSha256Sum. - Lockfiles and
gradle/verification-metadata.xmlhold no address. Keep them.
npm, pnpm, Yarn or Bun: the addresses
https://nexus.example.com/repository/<nexus name>/ becomes:
https://repo.example.com/npm/<name>/- Keep the slash at the end.
- Change scoped registries (
@acme:registry=) the same way. - Remove the Nexus
_auth,always-authandemaillines. - Yarn 2 and later: change
npmRegistryServerandnpmScopes.<scope>.npmRegistryServer. - Bun reads
.npmrc.
Then fix the lockfiles. In package-lock.json, pnpm-lock.yaml, bun.lock and a Yarn 1 yarn.lock, replace the Nexus prefix with the new one. Keep every integrity.
Run the frozen install of your tool:
npm ci # or:pnpm install --frozen-lockfileyarn install --immutablebun install --frozen-lockfilePython: the addresses
https://nexus.example.com/repository/<nexus name>/simple/ becomes:
https://repo.example.com/pypi/<name>/simple/Then renew the lockfiles with the lock command. Do not edit them by hand:
uv lock # or:poetry lock # Poetry 1: poetry lock --no-updateRead git diff. The versions and hashes stay the same; only the addresses change.
In requirements files with --hash, change only the index lines.
Rust and Cargo: the addresses
The index becomes:
sparse+https://repo.example.com/cargo/<name>/index/-
Keep the registry names. Change only
indexin[registries.<name>]. -
For a proxy or a group of crates.io, also add this:
.cargo/config.toml [source.crates-io]replace-with = "<name>" -
In
Cargo.lock, change eachsource =to the new index, exactly as in the config. Keep everychecksum.
Then run cargo fetch --locked.
.NET and NuGet: the addresses
https://nexus.example.com/repository/<nexus name>/index.json becomes:
https://repo.example.com/nuget/<name>/index.json- Keep the source keys, so
packageSourceMappingand the credentials still match. packages.lock.jsonholds no address. Keep it.
Docker and Helm: the addresses
The repository name is the first part of the image name:
nexus.example.com:8082/team/app:1.0 → repo.example.com/<name>/team/app:1.0- Through a proxy of Docker Hub,
nginx:1.27isrepo.example.com/<proxy name>/nginx:1.27. - Every
@sha256:digest stays the same.
Helm charts live in Docker repositories:
- A chart dependency on Nexus becomes
oci://repo.example.com/<name>. - Remove its
helm repo addline. - Ask your admin which Docker repository holds the charts.
- Run
helm dependency updateto renewChart.lock.
Debian or raw files: the addresses
An apt source changes like this:
deb https://nexus.example.com/repository/<nexus name>/ <dist> maindeb [signed-by=/etc/apt/keyrings/<name>.asc] https://repo.example.com/debian/<name> <dist> mainEvery package is in the component main. Ask your admin which signing key the repository has:
- The Nexus key: the machines trust it already.
- Another key: install
https://repo.example.com/debian/<name>/key.ascinto/etc/apt/keyrings/<name>.asc.
A raw address https://nexus.example.com/repository/<nexus name>/<path> becomes https://repo.example.com/raw/<name>/<path>.
Give the builds a read token
Section titled “Give the builds a read token”Skip this step when every repository is public.
-
Open Settings › Tokens, and click New read token.
-
Type a name, such as
my-app builds, and click Create token. -
Copy the token. It shows only once.
-
Put your username and the token in the variables below. Set them on your computer and as CI secrets.
When you choose the names, use TELESPHOREPO_READ_USER and TELESPHOREPO_READ_TOKEN. Never use TELESPHOREPO_TOKEN: the CLI uses that name when it publishes.
Gradle or Maven: the read token
Gradle: in the maven { } block, set a name and the credentials:
name = "myLibs"credentials(PasswordCredentials::class)Gradle then reads ORG_GRADLE_PROJECT_myLibsUsername and ORG_GRADLE_PROJECT_myLibsPassword. The name is the repository name as a Gradle identifier: my-libs is myLibs. In Groovy, write credentials(PasswordCredentials).
Maven: add a <server> whose <id> is the <id> of the repository or mirror. Put it in ~/.m2/settings.xml, and in a settings file that CI passes with -s:
<server> <id>maven-public</id> <username>${env.TELESPHOREPO_READ_USER}</username> <password>${env.TELESPHOREPO_READ_TOKEN}</password></server>npm, pnpm, Yarn or Bun: the read token
Set NPM_TOKEN to the read token, and add this line:
//repo.example.com/npm/<name>/:_authToken=${NPM_TOKEN}Yarn 2 and later: set npmAlwaysAuth: true and npmAuthToken: "${NPM_TOKEN}" instead.
Python: the read token
pip reads the credentials from the address. Put the whole address in the environment, and keep the plain address in the files:
export PIP_INDEX_URL="https://$TELESPHOREPO_READ_USER:$TELESPHOREPO_READ_TOKEN@repo.example.com/pypi/<name>/simple/"uv reads UV_INDEX_<NAME>_USERNAME and UV_INDEX_<NAME>_PASSWORD. <NAME> is the index name in capitals, with _ for every other character.
Poetry reads POETRY_HTTP_BASIC_<SOURCE>_USERNAME and POETRY_HTTP_BASIC_<SOURCE>_PASSWORD.
Rust and Cargo: the read token
Add credential-provider = "cargo:token" to the registry. Cargo then reads the token from CARGO_REGISTRIES_<NAME>_TOKEN: the registry name in capitals, with _ for -. This needs Cargo 1.74 or later.
.NET and NuGet: the read token
Add credentials under the source key, here dotnet:
<packageSourceCredentials> <dotnet> <add key="Username" value="%NUGET_USERNAME%" /> <add key="ClearTextPassword" value="%NUGET_TOKEN%" /> </dotnet></packageSourceCredentials>Docker and Helm: the read token
On your computer, use the credential helper of the CLI instead of a token:
-
Install the CLI, and run
telesphorepo login https://repo.example.com. -
Add the helper to
~/.docker/config.json, and keep the other keys:~/.docker/config.json {"credHelpers": {"repo.example.com": "telesphorepo"}}
Docker, Podman, Helm and ORAS then pull with no docker login.
In CI and on servers, log in with the read token:
printf '%s' "$TELESPHOREPO_READ_TOKEN" | docker login repo.example.com -u "$TELESPHOREPO_READ_USER" --password-stdinIn Kubernetes, make a pull secret with kubectl create secret docker-registry.
Debian or raw files: the read token
apt reads credentials only from /etc/apt/auth.conf.d/. Run these lines. They ask for the token without showing it:
printf 'Read token: '; read -rs READ_TOKEN; echosudo install -m 600 /dev/null /etc/apt/auth.conf.d/<name>.confprintf 'machine %s login <user> password %s\n' https://repo.example.com/debian/<name> "$READ_TOKEN" \ | sudo tee -a /etc/apt/auth.conf.d/<name>.conf > /dev/nullcurl: pass the credentials on stdin, never as an argument:
printf 'user = "%s:%s"\n' "$TELESPHOREPO_READ_USER" "$TELESPHOREPO_READ_TOKEN" | curl -fsSL -K - -o <file> <address>Check the build
Section titled “Check the build”-
Search again for the Nexus host and
/repository/. Find out why each hit is still there. -
Build with an empty cache, as below.
-
Check in the output that every download comes from
repo.example.com. -
Commit the changes.
Gradle or Maven: the check
./gradlew build --refresh-dependenciesmvn -U -Dmaven.repo.local=/tmp/empty-m2 verifynpm, pnpm, Yarn or Bun: the check
Remove node_modules, then run the frozen install, such as npm ci.
Python: the check
In a new virtual environment:
pip install --no-cache-dir -r requirements.txt # or: uv sync, poetry installRust and Cargo: the check
cargo fetch --lockedcargo build.NET and NuGet: the check
dotnet nuget locals all --cleardotnet restoreDocker and Helm: the check
docker build --no-cache --pull .Then pull each image of the compose files and manifests.
Debian or raw files: the check
Run sudo apt update, then install a package. Download each raw file with curl, and compare its SHA-256.
If you move only the downloads, you are done.
Move publishing
Section titled “Move publishing”The copies refuse uploads until the switch-over. So the project first publishes to a test repository.
Ask your admin for two things:
- A test repository: a hosted repository of the same format, such as
maven-releases-rehearsal, where your account has Publish. - A CI pipeline for the project, with the test repository and the real one. See Ask for a CI pipeline.
Then change the project:
-
Remove the Nexus publishing from the project and its CI, as below. Keep the steps that build the artifacts.
-
Open the test repository, then the Publish to this repository tab.
-
Click Set up a project, and follow Publish from a project. The CLI writes
telesphorepo.tomland, if you want, a CI file. -
Delete the CI secrets that only Nexus used.
Gradle or Maven: what to remove
- The Nexus
<distributionManagement>entries and<server>credentials - Nexus staging plugins, such as
nexus-staging-maven-pluginandio.github.gradle-nexus.publish-plugin - The Nexus repository in
publishing { repositories { } }
Keep maven-publish, the publications, group and version. telesphorepo publish runs mvn deploy or gradle publish with the repository as the target.
npm, pnpm, Yarn or Bun: what to remove
publishConfig.registry- The Nexus publish auth lines
- The
npm publishsteps of CI and their secrets
telesphorepo publish picks the tool by the lockfile. In a workspace, put a telesphorepo.toml in the folder of each package that publishes.
Python: what to remove
.pypircandtwine uploaduv publishandpublish-urlpoetry publish -rand the Poetry repository for Nexus
telesphorepo publish builds with Poetry, uv or python -m build, then uploads every wheel and sdist.
Rust and Cargo: what to remove
- The
cargo publish --registry <nexus>steps and the Nexus tokens
Where Cargo.toml names Nexus in publish =, set publish = ["telesphorepo"]. Keep the key: without it, a plain cargo publish goes to crates.io.
.NET and NuGet: what to remove
- The
dotnet nuget pushornuget pushsteps to Nexus <apikeys>and the API key secrets
telesphorepo publish runs dotnet pack -c Release and pushes each package.
Docker and Helm: what to remove
docker loginto Nexus and the Nexus push credentialshelm pushandcurluploads to Nexus
Images push with docker push repo.example.com/<repo>/<image>:<tag> through the credential helper. They need no telesphorepo.toml. A chart folder gets one, and telesphorepo publish packages and pushes the chart.
Debian or raw files: what to remove
- The
curl -uuploads to Nexus and their passwords
telesphorepo publish <file> uploads the files.
Rehearse publishing
Section titled “Rehearse publishing”-
In the project folder, run
telesphorepo status. Check the command it names. -
Publish to the test repository:
Terminal window telesphorepo publish -
In a new empty folder outside the project, install what you published, as below. Then remove the folder.
-
To rehearse CI too, push to a branch that the CI pipeline lists. Check the run.
Gradle or Maven: the install check
Write a small build that depends on the group:artifact:version you published, from https://repo.example.com/maven/<test repository>. Build it with an empty cache.
npm, pnpm, Yarn or Bun: the install check
Run npm init -y, and write an .npmrc with the test registry. Then run npm install <package>@<version>.
Python: the install check
In a new virtual environment:
pip install --index-url https://repo.example.com/pypi/<test repository>/simple/ \ --extra-index-url https://pypi.org/simple/ <package>==<version>Rust and Cargo: the install check
Run cargo new verify, and write a .cargo/config.toml with the test registry. Then run cargo add <crate>@<version> --registry <name> and cargo build.
.NET and NuGet: the install check
Run dotnet new console, and write a nuget.config with the test source. Then run dotnet add package <id> --version <version>.
Docker and Helm: the install check
Remove the local image, then pull it:
docker pull repo.example.com/<test repository>/<image>:<tag>helm pull oci://repo.example.com/<test repository>/<chart> --version <version>Debian or raw files: the install check
For raw files, download the file with curl, and compare its SHA-256. For Debian, add the test source and its key in a throwaway debian container. Then run apt-get update and apt-get install <package>.
After the switch-over
Section titled “After the switch-over”When your admin has switched over, the copies take uploads.
-
Set
repointelesphorepo.tomlto the real repository:telesphorepo.toml server = "https://repo.example.com"repo = "maven-releases" -
Check that the CI pipeline lists the real repository.
-
Publish once.
-
Delete the test repository.
If something goes wrong
Section titled “If something goes wrong”The publish answers 403
Your account lacks Publish on the repository. Ask your admin for it.
The Tokens page has no New read token
You sign in with single sign-on, and such an account has no read tokens. Ask your admin for an account with a password for the builds.
CI does not find telesphorepo.toml
Run git check-ignore telesphorepo.toml. If git ignores the file, CI never sees it. Change .gitignore, then commit the file.
The server runs on plain http://
Each tool needs the host allowed:
- Gradle:
isAllowInsecureProtocol = true. Maven refuseshttp://other thanlocalhost. - pip:
trusted-host = <host>. uv:allow-insecure-host. - Yarn: the host in
unsafeHttpWhitelist. - NuGet:
allowInsecureConnections="true"on the source. - Docker: the host in
insecure-registriesof/etc/docker/daemon.json.
Gradle or Maven: problems
Public dependencies stop resolving
A hosted repository holds only its own packages. Keep mavenCentral(), or use a group with a proxy of Maven Central.
npm, pnpm, Yarn or Bun: problems
Yarn 4 does not install a new version
On a hosted repository, add npmMinimalAgeGate: 0, or Yarn 4 waits a week. On a group or a proxy, list your own packages in npmPreapprovedPackages instead.
A group misses versions of a package
In an npm group, the first member that has a package name serves every version of it. Nexus merges them. If you need versions of one name from two members, ask your admin.
npm audit fails
Telesphorepo does not serve npm audit. Run it with --registry=https://registry.npmjs.org/.
Python: problems
Dependencies stop resolving
A hosted repository holds only your own packages. Use a group with a proxy of PyPI, or PyPI itself. In a group, the first member that has a project name serves every file of it.
Rust and Cargo: problems
A group misses versions of a crate
In a Cargo group, the first member that has a crate name serves every version of it.
.NET and NuGet: problems
A group misses versions of a package
In a NuGet group, the first member that has a package id serves every version of it.
The source key starts with a digit
In <packageSourceCredentials>, write such a key as _x003N_....
Docker and Helm: problems
CI cannot pull a private base image
With its identity token, a CI job reads only what anyone can read. Log in with the read token before the build.
docker pull asks for a login on your computer
telesphorepo login does not add the credential helper. Run telesphorepo status: it shows whether ~/.docker/config.json has the entry.