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[]):

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 src/Acme.Analytics.Api/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": ["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 .csproj inside 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 ref to a commit SHA (git ls-remote).
  • Clone at that SHA into .kanject/cache/<repo>-<sha>/.
  • Write deterministic kanject.g.props project-reference injection and kanject.g.targets validation under kanject-cli/.
  • Leave every consumer csproj untouched — Directory.Build.props / .targets import the generated wiring once.
  • Pin each repository, requested ref, resolved SHA, project path, and consumer edge in manifest.lock.json.
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 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 ref re-resolves to the latest commit on every fresh sync; 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 with kanject migrate. It scans for ..\..\sibling.csproj ProjectReferences, derives each repo's remote URL, and folds them into dependencies[] — 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 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.
  • migrate adopts existing references; msbuild enable wires the hooks; private repos use your own git credentials.
Was this page helpful?