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[]):
# 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 src/Acme.Analytics.Api/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": ["src/Acme.Analytics.Api/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 turns every declared source into pinned, build-ready project wiring:
- Resolve each
refto a commit SHA (git ls-remote). - Clone at that SHA into
.kanject/cache/<repo>-<sha>/. - Write deterministic
kanject.g.propsproject-reference injection andkanject.g.targetsvalidation underkanject-cli/. - Leave every consumer csproj untouched —
Directory.Build.props/.targetsimport the generated wiring once. - Pin each repository, requested ref, resolved SHA, project path, and consumer edge in
manifest.lock.json.
# 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 byte-deterministic. In CI, kanject sync --locked fails if the lock has drifted; --offline replays the lock and requires the cache to be hydrated.
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 adds no auth layer of its own; if git clone <repository> works in that shell, sync works.
Guidelines
- Pin prod-critical deps to a tag or SHA. A branch
refre-resolves to the latest commit on every fresh sync; 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 with
kanject migrate. It scans for..\..\sibling.csprojProjectReferences, derives each repo's remote URL, and folds them intodependencies[]— non-destructively. - Wire the git hooks once (
kanject msbuild enable). Then teammates auto-sync after pulling a manifest change instead of debugging a stale build. - 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. migrateadopts existing references;msbuild enablewires the hooks; private repos use your own git credentials.