Dependency Sources & Resolution

View .md

Most services depend on shared libraries living in other git repos. A plain ..\..\sibling.csproj reference breaks on a fresh runner. Declare each dependency by git URL, ref, and project path; kanject sync clones the pinned commit and emits deterministic MSBuild imports under kanject-cli/ without packing packages or editing consumer csprojs.

You'll learn
  • Declare a cross-repo dependency by its source — git URL, ref, and project path
  • Understand how kanject sync resolves it: ref → SHA → cache → generated MSBuild imports → lock
  • Keep the dependency graph reproducible with the lockfile, on every machine and in CI
  • Authenticate private dependency repos and adopt existing ProjectReferences

The problem it solves

A cross-repo <ProjectReference Include="..\..\Acme.Identity\…csproj" /> resolves only when the sibling repo happens to be at the expected path. A clean CI checkout does not have that layout. The fix is a manifest-declared source, a locked SHA, a predictable cache location, and generated project-reference wiring.

Declare the source

A dependency's source is a git repository at a ref, the csproj inside it, and the consumers that reference it. Declare it with kanject add lib (or by hand in manifest.json → dependencies[]). Consumer paths are relative to the service root, where kanject-cli/ lives:

bash
# Register a cross-repo library and wire the consumer that uses itkanject add lib \  --repository git@github.com:acme/acme-platform.git \  --ref main \  --project services/identity/src/Acme.Identity.Data/Acme.Identity.Data.csproj \  --consumer Acme.Analytics.Api.csproj

That appends a dependencies[] entry and runs sync (pass --no-sync to skip):

json
"dependencies": [  {    "name": "Acme.Identity.Data",    "repository": "git@github.com:acme/acme-platform.git",    "ref": "main",    "projectPath": "services/identity/src/Acme.Identity.Data/Acme.Identity.Data.csproj",    "consumers": ["Acme.Analytics.Api.csproj"]  }]
  • name — display name for the dependency in the manifest.
  • repository — the git URL (https or ssh).
  • ref — a branch, tag, or 40-char commit SHA to resolve.
  • projectPath — path to the .csproj inside the cloned repo.
  • consumers — the consumer csproj(s) in this service that reference it.

Resolve it: kanject sync

kanject sync resolves each ref to a commit, clones that commit into .kanject/cache/, and writes generated MSBuild files under kanject-cli/ that wire the project in. Consumer csprojs are never edited, and nothing is packed. Each repository, ref, resolved commit, project path and consumer is pinned in manifest.lock.json. On a developer machine, sync also adds any cross-repo <ProjectReference> you added by hand to dependencies[].

bash
# Resolve, cache, and wire every declared dependencykanject sync​# CI: fail fast if the lockfile is out of date with the manifestkanject sync --locked​# Offline: trust the lockfile + cache, skip the network entirelykanject sync --offline

Reproducible builds: the lockfile

Commit manifest.lock.json, kanject.g.props, and kanject.g.targets. Together they pin the graph and make the wiring deterministic. Plain kanject sync re-resolves every ref, so a branch ref moves to its latest commit and the lock changes with it. kanject sync --locked re-resolves too, then fails with KANCLI196 if the result differs from the committed lock, which a moved branch alone triggers. --offline skips the network and replays the lock, and needs the cache already filled.

In CI, and for private repos

On a fresh runner, run sync before build so restore finds every dependency — see the CI/CD Pipeline page, where the build stage does exactly this. sync clones with your own git credentials: SSH keys or a credential helper locally, and in CI a token — the pipeline stores a GitHub PAT in Secrets Manager so CodeBuild can clone private dependency repos. Kanject CLI adds no auth layer of its own; if git clone <repository> works in that shell, sync works.

Guidelines

  • Pin dependencies to a tag or SHA. A branch ref re-resolves to the latest commit on every sync, and fails --locked in CI as soon as the branch moves; a tag or SHA is stable.
  • Commit manifest.lock.json and review its diffs. It is the contract that makes CI reproducible — a changed SHA in the lock is a dependency bump worth seeing in review.
  • Run kanject sync --locked in CI. It turns an out-of-date lock into a fast, clear failure instead of a surprise dependency upgrade.
  • Adopt existing references. kanject sync folds hand-added sibling ProjectReferences into dependencies[] on a developer machine; kanject migrate --dry-run previews the same conversion as a one-off.
  • Wire the git hooks once (kanject msbuild enable) when kanject-cli/ is at the repository root, so teammates re-sync after any pull that changes the lock.
  • Never commit .kanject/. The clone cache and sync sentinel are derived artifacts; delete and re-sync any time. Do commit the generated files under kanject-cli/.
  • Configure git credentials before syncing a private repo. SSH or a credential helper locally; a PAT in CI.
Recap
  • A cross-repo dependency's source is a git repo at a ref plus a csproj; declare it in manifest.json → dependencies[] (or via kanject add lib).
  • kanject sync clones at the pinned commit, writes generated MSBuild imports without touching consumer csprojs, and pins manifest.lock.json.
  • The lockfile makes the graph reproducible everywhere; --locked enforces it in CI, --offline skips the network.
  • sync adopts hand-added references on a developer machine; msbuild enable wires the hooks; private repos use your own git credentials.
Was this page helpful?