Dependency Sources & Resolution
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.
- Declare a cross-repo dependency by its source — git URL, ref, and project path
- Understand how
kanject syncresolves 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:
# 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):
"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.csprojinside 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[].
# 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
refre-resolves to the latest commit on every sync, and fails--lockedin CI as soon as the branch moves; a tag or SHA is stable. - Commit
manifest.lock.jsonand 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 --lockedin CI. It turns an out-of-date lock into a fast, clear failure instead of a surprise dependency upgrade. - Adopt existing references.
kanject syncfolds hand-added siblingProjectReferences intodependencies[]on a developer machine;kanject migrate --dry-runpreviews the same conversion as a one-off. - Wire the git hooks once (
kanject msbuild enable) whenkanject-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 underkanject-cli/. - Configure git credentials before syncing a private repo. SSH or a credential helper locally; a PAT in CI.
- A cross-repo dependency's source is a git repo at a ref plus a csproj; declare it in
manifest.json → dependencies[](or viakanject add lib). kanject syncclones at the pinned commit, writes generated MSBuild imports without touching consumer csprojs, and pinsmanifest.lock.json.- The lockfile makes the graph reproducible everywhere;
--lockedenforces it in CI,--offlineskips the network. syncadopts hand-added references on a developer machine;msbuild enablewires the hooks; private repos use your own git credentials.