Kepler 1.0.0

dotnet tool install --global Kepler --version 1.0.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Kepler --version 1.0.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Kepler&version=1.0.0
                    
nuke :add-package Kepler --version 1.0.0
                    

Kepler

Kepler is a .NET global tool for planning, packing, publishing, and confirming coordinated NuGet releases from a multi-project repository. It persists the internal dependency graph discovered during scan and reuses its dependency levels throughout the release, so packages are always processed from foundations to dependents.

Requirements and conventions

  • .NET SDK 10 or later.
  • Package projects under src/**/*.csproj.
  • A package project must define <Version> and must not set <IsPackable>false</IsPackable>.
  • Central package management through Directory.Packages.props at the repository root.
  • NuGet.org is currently the fixed remote source: https://api.nuget.org/v3/index.json.
  • Run every command from the repository root. Paths in .kepler are calculated from the current directory.

Kepler currently has no configuration command or configuration-file arguments. Project discovery, remote source, and workspace paths are conventions encoded by the tool.

Installation

From this repository:

dotnet pack src/keplercli/keplercli.csproj --configuration Release
dotnet tool install --global --add-source src/keplercli/bin/Release Kepler

To update an existing installation from a newly generated package:

dotnet tool update --global --add-source src/keplercli/bin/Release Kepler

Confirm the command is available:

kepler --help

Workspace and persisted state

kepler init creates a local .kepler workspace:

.kepler/
├── nuget.config
├── package-list.json
└── packages/
  • nuget.config puts the local packages directory before NuGet.org. This allows a dependent package to restore a package produced earlier in the same release.
  • package-list.json is the release state machine. It stores package paths, local and remote versions, internal dependencies, dependency levels, publication decisions, and publication state.
  • packages/ contains the generated .nupkg files until check confirms them remotely.

The entire .kepler/ directory is an execution artifact and must remain excluded from source control. Directory.Packages.props, however, belongs to the repository and is modified by pack so downstream projects restore the newly packed versions.

kepler init
kepler scan
kepler plan --dry-run
kepler plan
kepler pack
kepler publish --dry-run
kepler publish --api-key <NUGET_API_KEY>
kepler check --wait --timeout 00:20:00 --interval 00:00:30

Run the commands in this order. With the exception of init, commands require an initialized .kepler workspace.

Commands

kepler init

Initializes the local Kepler workspace. It has no arguments or options.

kepler init

It performs the following operations:

  1. Creates .kepler/ and .kepler/packages/ when they do not exist.
  2. Writes .kepler/nuget.config with the local kepler source first and NuGet.org second.
  3. Creates or attaches .kepler/package-list.json.
  4. Prints every generated path.

Running init again is safe: it ensures the required directories and sources exist without deleting package state.

kepler scan

Discovers package projects and synchronizes dependency and remote-version state. It has no arguments or options.

kepler scan

For every src/**/*.csproj, scan:

  1. Skips the project when <IsPackable>false</IsPackable> is set.
  2. Treats a project with <Version> as packable. A project explicitly marked packable but missing <Version> is rejected.
  3. Resolves the package ID from <PackageId>, then <AssemblyName>, and finally the project filename.
  4. Reads ProjectReference, PackageReference, and PackageVersion relationships.
  5. Builds the internal dependency graph and calculates each package's dependency level.
  6. Queries NuGet.org for the latest published version.
  7. Replaces the scanned package inventory in .kepler/package-list.json and records synchronization timestamps.

Duplicate package IDs or invalid project XML stop the command. Because scan replaces the discovered inventory, do not run it in the middle of an unfinished Pending/Pushed publication flow.

kepler plan [--dry-run]

Calculates which packages must be released using the dependencies and dependency levels persisted by scan.

kepler plan
kepler plan --dry-run

Options:

Option Value Default Effect
--dry-run Boolean switch false Prints and validates the plan without modifying package-list.json.

A package becomes a release root when it has never been published or its local version is greater than the remote version. Every transitive dependent of a release root is also selected.

Version rules:

  • First publication: keeps the version declared by the project.
  • Explicit local increment: keeps the newer local version.
  • Dependency-only change: increments the patch component automatically.
  • Local version lower than the remote version: validation fails and nothing is written.

Without --dry-run, selected packages are marked Pending and ShouldPublish=true. Their planned version and output path are persisted. Unselected packages are marked as already published for this plan.

kepler pack

Builds and packs every Pending package selected by plan. It has no arguments or options.

kepler pack

Before packing, Kepler refuses to continue if any package is Pushed and still awaits confirmation. It then starts from a clean local state:

  1. Empties .kepler/packages/.
  2. Restores every managed internal package in Directory.Packages.props to its known remote version; entries without a remote version are removed.
  3. Orders selected packages by dependency level and then package ID.
  4. Runs dotnet build <project> --configuration Release with forced restore against .kepler/nuget.config.
  5. Runs dotnet pack <project> --configuration Release --no-build --no-restore, forcing the planned package version and writing directly to .kepler/packages/.
  6. Updates Directory.Packages.props immediately after each successful package, allowing later dependency levels to restore that local version.

A blank line separates dependency levels in the console output.

If any build, pack, or expected-file check fails, Kepler empties the package directory and restores Directory.Packages.props to the remote-version baseline. A new pack invocation therefore starts from zero.

kepler publish --api-key <key> [--dry-run]

Validates and pushes packages created by pack to NuGet.org in publication order.

kepler publish --dry-run
kepler publish --api-key <NUGET_API_KEY>

Options:

Option Value Required Effect
--api-key String Required unless --dry-run is used or no package remains Pending Passed to dotnet nuget push. It is not written to Kepler files.
--dry-run Boolean switch No Executes strict preflight and prints publication order without pushing or changing state.

Strict preflight runs before any push and requires:

  • Exactly one expected .nupkg for every package selected by the plan.
  • No unexpected .nupkg files in .kepler/packages/.
  • Exactly one .nuspec inside each package.
  • Package ID and semantic version matching package-list.json.

After each successful NuGet push, Kepler immediately persists that package as Pushed. It does not delete the file and does not assume that the package is already queryable from NuGet.org.

publish is resumable but an actual push is not inherently idempotent. On a retry, Kepler skips packages already marked Pushed and continues with those still Pending. If a push succeeds remotely but the process terminates before state is saved, confirm the remote state before retrying to avoid submitting the same immutable version again.

The API key is not persisted by Kepler, but it is supplied as a child-process argument. In CI, pass it from a masked secret and ensure command/process diagnostics do not expose arguments.

kepler check [--wait] [--timeout <duration>] [--interval <duration>]

Confirms that Pushed package versions are visible through the NuGet remote API.

kepler check
kepler check --wait
kepler check --wait --timeout 00:20:00 --interval 00:00:30

Options:

Option Value Default Effect
--wait Boolean switch false Repeats checks until all packages are visible or the timeout expires.
--timeout .NET TimeSpan 00:15:00 Maximum total wait. Must be greater than zero. Primarily meaningful with --wait.
--interval .NET TimeSpan 00:00:30 Delay between remote checks. Must be greater than zero.

Without --wait, check performs one pass and returns success even when some packages remain pending; the summary reports how many are still missing. This makes it suitable for polling across separate pipeline runs.

With --wait, it polls in the same process. For every version found remotely, Kepler:

  1. Marks the package Published.
  2. Copies its local version into RemoteVersion.
  3. Clears ShouldPublish.
  4. Saves package-list.json.
  5. Deletes the confirmed .nupkg from .kepler/packages/.

On timeout, check fails and lists the versions still missing. Their state remains Pushed and their package files remain available, so the command can safely be run again.

Publication state model

State Meaning Next command
Pending Planned and not yet successfully pushed. pack, then publish
Pushed NuGet accepted the push, but Kepler has not confirmed remote visibility. check
Published The exact version is visible remotely and local release artifacts were finalized. Start the next release with scan

CI/CD example

Use a secret provided by the CI platform and let check --wait absorb NuGet indexing delay:

kepler init
kepler scan
kepler plan
kepler pack
kepler publish --dry-run
kepler publish --api-key "$NUGET_API_KEY"
kepler check --wait --timeout 00:20:00 --interval 00:00:30

Persist .kepler/package-list.json and .kepler/packages/ between retryable pipeline jobs if publish and check run in different jobs or runners. Do not commit .kepler/.

After check succeeds, review and commit the resulting Directory.Packages.props changes separately. Kepler intentionally does not stage or commit repository files.

Recovery guide

  • Kepler is not initialized: run kepler init from the repository root.
  • Plan reports a local version below remote: update the project version, then run scan and plan again.
  • Pack fails: fix the project and rerun kepler pack; failed output and central versions have already been rolled back.
  • Publish preflight fails: rerun pack to recreate an exact, clean package set.
  • Publish stops after some packages: rerun publish with the API key; packages recorded as Pushed are skipped.
  • Check times out: rerun check --wait; no pushed package is resent.
  • A package was accepted remotely but remains Pending locally: verify that exact version on NuGet.org before retrying. NuGet package versions are immutable.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
1.0.0 93 7/12/2026