# 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.

**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 `ProjectReference`s

## 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 it
kanject 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 dependency
kanject sync

# CI: fail fast if the lockfile is out of date with the manifest
kanject sync --locked

# Offline: trust the lockfile + cache, skip the network entirely
kanject sync --offline
```

> **Sync runs itself:** You rarely run sync by hand. add lib runs it, and the git hooks installed by kanject msbuild enable (post-merge / post-checkout) re-sync after a pull or branch switch — so a teammate who pulls a manifest change gets the new dependency wired automatically.

## 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](https://www.kanject.com/docs/cli-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` `ProjectReference`s, 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.

---
_Source: https://www.kanject.com/docs/cli-dependencies/ · Kanject Docs_
