Skip to content

Migrating to a Registry-Based Dependency Setup

This guide walks an organization that maintains multiple library projects sharing a single internal package collection through replacing git+branch and zip+local-path dependency declarations with version + registry resolution backed by a shared index repository.

The running example is a hypothetical "comm-group" organization: a collection of internal C++ libraries (stdcomm, foundrycomm, logcomm, several leaf consumers) that today depend on each other through pinned Git branches. The same shape applies to any setup where a dozen sibling projects need to coordinate releases without spinning up a hosted package server.

What you get

Before: every consumer's CCGO.toml carries the Git URL and the "distribution branch" of every dep, so a new release of stdcomm triggers a coordinated branch update across every downstream CCGO.toml. Branches like dist-v1.0.0 move under your feet whenever the upstream tagger force-updates them.

[[dependencies]]
name = "stdcomm"
git = "git@git.example.com:org/stdcomm.git"
branch = "dist-v1.0.0"
version = "1.0.0"

[[dependencies]]
name = "logcomm"
git = "git@git.example.com:org/logcomm.git"
branch = "dist-v1.0.0"
version = "1.0.0"

After: every consumer points at one shared index repo, then names each dep by exact version. The index records the artifact archive URL and a SHA-256 checksum per version.

[registries]
org = "git@git.example.com:org/ccgo-index.git"

[[dependencies]]
name = "stdcomm"
version = "25.2.9519653"
registry = "org"

[[dependencies]]
name = "logcomm"
version = "25.2.9519653"
registry = "org"
  • Pinned by exact version. No moving dist-v... branches.
  • One shared index lists every published version with its checksum.
  • Consumers download a single archive zip from a CDN. No git history transfer, no post-clone build step on the consumer side.
  • Adding a new sibling library requires zero changes to existing consumers — only a new ccgo publish index run from the new project.

Step 1 — Bootstrap the index repo

Create one Git repository to hold the package index. It starts empty and gets populated by every project's ccgo publish index runs.

# On your Git host: create a fresh repo, e.g. org/ccgo-index.git
# Locally, give it one initial commit so `git clone` works:
git clone git@git.example.com:org/ccgo-index.git
cd ccgo-index

cat > index.json <<'EOF'
{
  "name": "org-index",
  "description": "Internal package index for org libraries",
  "version": "1",
  "package_count": 0
}
EOF

git add index.json
git commit -m "init: empty index"
git push origin master

That's it for bootstrap. The directory layout (one JSON per package, sharded by name) is created lazily by each ccgo publish index call.

Step 2 — Per-project CI workflow

For every library in the collection, replace the existing release step (probably "tag, push, branch update") with a build → package → upload → publish-index sequence. The shape:

# In the library's repo:

# 1. Build all platforms.
ccgo build all --release

# 2. Package the build outputs into a zip whose layout matches what
#    `ccgo fetch` extracts into .ccgo/deps/<name>/.
ccgo package --release
# Produces: target/release/<plat>/<NAME>_CCGO_PACKAGE-<version>.zip

# 3. Upload the zip to your CDN / artifactory.
#    (Plug in your existing release upload script. The destination URL
#    must match the template you'll pass to `ccgo publish index`.)
your-upload-script target/release/macos/STDCOMM_CCGO_PACKAGE-25.2.9519653.zip \
  https://artifacts.example.com/stdcomm/

# 4. Append the new version to the shared index. Each `ccgo publish index`
#    call publishes EXACTLY ONE version (append-only — duplicates are
#    rejected). Use `--index-version` (and/or `--index-tag` when your tag
#    convention isn't `v<version>`).
ccgo publish index \
  --index-repo git@git.example.com:org/ccgo-index.git \
  --index-name org-index \
  --index-version 25.2.9519653 \
  --archive-url-template "https://artifacts.example.com/{name}/{name}_CCGO_PACKAGE-{version}.zip" \
  --checksum \
  --index-push

The {name}, {version}, and {tag} placeholders are substituted in the template. Run this same command from every library each time it ships a new release; the index repo accumulates entries naturally — old versions are preserved across publishes, ccgo only appends and sorts.

Step 3 — Migrate consumer CCGO.toml

In each consumer project, replace existing git/branch (or zip/local-path) dep declarations with version + registry:

+[registries]
+org = "git@git.example.com:org/ccgo-index.git"
+
 [[dependencies]]
 name = "stdcomm"
-git = "git@git.example.com:org/stdcomm.git"
-branch = "dist-v1.0.0"
-version = "1.0.0"
+version = "25.2.9519653"
+registry = "org"

Then run ccgo fetch to confirm the dep resolves through the index. The generated CCGO.lock will record source = "registry+git@..." plus the checksum so the next ccgo fetch --locked reproduces the exact bytes.

If a single consumer pulls deps from more than one collection, declare both registries in [registries] and let each [[dependencies]] entry pin itself with its own registry = "...". Per-dep selectors override TOML iteration order.

Step 4 — Roll out (deepest dep first)

Migrate libraries in dependency-graph order, starting with the leaves. That way, by the time you migrate a library that depends on stdcomm, stdcomm is already publishable through the index — the two changes never have to land in the same PR.

A typical rollout:

  1. stdcomm (no internal deps). Adopt the publish-to-index CI step. Existing consumers keep using git/branch for now — stdcomm's own CCGO.toml has no consumer-facing changes.
  2. foundrycomm, logcomm (depend on stdcomm). First, migrate their consumer side: their CCGO.toml now references stdcomm by version + registry. Confirm with ccgo fetch + ccgo build. Then adopt the publish-to-index CI step so they themselves become resolvable through the index.
  3. Leaf consumers (depend on foundrycomm / logcomm). Migrate their CCGO.toml to point at the index. By this point, every dep they need is in the index.

You don't have to do the whole graph in one pass. Mixing old-style git/branch and new-style version/registry in the same CCGO.toml is supported — they're independent branches in the resolution priority list.

Rollback

Legacy git/branch and zip declarations keep working unchanged. The registry tier is opt-in and only fires when (a) [registries] is non-empty AND (b) a dependency has no explicit path/git/zip source. If a project hits trouble — index repo unreachable, archive URL down, checksum mismatch — reverting that project's CCGO.toml back to the old git = "..." / branch = "..." form is a one-line revert. No infrastructure unwind required.

You can also migrate one dependency at a time within a single CCGO.toml. Keep most deps on git / branch, switch the riskiest one to version + registry, and confirm that branch in CI before moving the next one over.

See also