dotnet-cli-packaging

Multi-platform packaging for .NET CLI tools: Homebrew formula authoring (binary tap and cask), apt/deb packaging with dpkg-deb , winget manifest YAML schema and PR submission to winget-pkgs , Scoop manifest JSON, Chocolatey package creation, dotnet tool global/local packaging, and NuGet distribution.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "dotnet-cli-packaging" with this command: npx skills add novotnyllc/dotnet-artisan/novotnyllc-dotnet-artisan-dotnet-cli-packaging

dotnet-cli-packaging

Multi-platform packaging for .NET CLI tools: Homebrew formula authoring (binary tap and cask), apt/deb packaging with dpkg-deb , winget manifest YAML schema and PR submission to winget-pkgs , Scoop manifest JSON, Chocolatey package creation, dotnet tool global/local packaging, and NuGet distribution.

Version assumptions: .NET 8.0+ baseline. Package manager formats are stable across .NET versions.

Scope

  • Homebrew formula authoring (binary tap and cask)

  • apt/deb packaging with dpkg-deb

  • winget manifest YAML schema and PR submission

  • Scoop manifest JSON for Windows

  • Chocolatey package creation

  • dotnet tool global/local packaging and NuGet distribution

Out of scope

  • CLI distribution strategy (AOT vs framework-dependent decision) -- see [skill:dotnet-cli-distribution]

  • Release CI/CD pipeline that automates packaging -- see [skill:dotnet-cli-release-pipeline]

  • Native AOT compilation -- see [skill:dotnet-native-aot]

  • Container-based distribution -- see [skill:dotnet-containers]

  • General CI/CD patterns -- see [skill:dotnet-gha-patterns] and [skill:dotnet-ado-patterns]

Cross-references: [skill:dotnet-cli-distribution] for distribution strategy and RID matrix, [skill:dotnet-cli-release-pipeline] for automated package publishing, [skill:dotnet-native-aot] for AOT binary production, [skill:dotnet-containers] for container-based distribution, [skill:dotnet-tool-management] for consumer-side tool installation and manifest management.

Homebrew (macOS / Linux)

Homebrew is the primary package manager for macOS and widely used on Linux. Two distribution formats exist for CLI tools.

Binary Tap (Formula)

A formula downloads pre-built binaries per platform. This is the recommended approach for Native AOT CLI tools.

Formula/mytool.rb

class Mytool < Formula desc "A CLI tool for managing widgets" homepage "https://github.com/myorg/mytool" version "1.2.3" license "MIT"

on_macos do on_arm do url "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-osx-arm64.tar.gz" sha256 "abc123..." end # Optional: remove on_intel block if not targeting Intel Macs on_intel do url "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-osx-x64.tar.gz" sha256 "def456..." end end

on_linux do on_arm do url "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-linux-arm64.tar.gz" sha256 "ghi789..." end on_intel do url "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-linux-x64.tar.gz" sha256 "jkl012..." end end

def install bin.install "mytool" end

test do assert_match version.to_s, shell_output("#{bin}/mytool --version") end end

Hosting a Tap

A tap is a Git repository containing formulae. Create a repo named homebrew-tap :

myorg/homebrew-tap/ Formula/ mytool.rb

Users install with:

brew tap myorg/tap brew install mytool

Homebrew Cask

Casks are for GUI applications or tools with an installer. For pure CLI tools, prefer formulae over casks.

Casks/mytool.rb -- only if the tool has a GUI component

cask "mytool" do version "1.2.3" sha256 "abc123..."

url "https://github.com/myorg/mytool/releases/download/v#{version}/mytool-#{version}-osx-arm64.tar.gz" name "MyTool" homepage "https://github.com/myorg/mytool"

binary "mytool" end

apt/deb (Debian/Ubuntu)

Building a .deb Package with dpkg-deb

Create the package directory structure:

mytool_1.2.3_amd64/ DEBIAN/ control usr/ bin/ mytool

Control file:

Package: mytool Version: 1.2.3 Section: utils Priority: optional Architecture: amd64 Maintainer: My Org <dev@myorg.com> Description: A CLI tool for managing widgets MyTool provides fast widget management from the command line. Built with .NET Native AOT for zero-dependency execution. Homepage: https://github.com/myorg/mytool

Build the package:

#!/bin/bash set -euo pipefail

VERSION="${1:?Usage: build-deb.sh <version>}" ARCH="amd64" # or arm64 PKG_DIR="mytool_${VERSION}_${ARCH}"

mkdir -p "$PKG_DIR/DEBIAN" mkdir -p "$PKG_DIR/usr/bin"

Copy the published binary

cp "artifacts/linux-x64/mytool" "$PKG_DIR/usr/bin/mytool" chmod 755 "$PKG_DIR/usr/bin/mytool"

Write control file

cat > "$PKG_DIR/DEBIAN/control" << EOF Package: mytool Version: ${VERSION} Section: utils Priority: optional Architecture: ${ARCH} Maintainer: My Org <dev@myorg.com> Description: A CLI tool for managing widgets Homepage: https://github.com/myorg/mytool EOF

Build the .deb

dpkg-deb --build --root-owner-group "$PKG_DIR" echo "Built: ${PKG_DIR}.deb"

RID to Debian architecture mapping:

.NET RID Debian Architecture

linux-x64

amd64

linux-arm64

arm64

Installing the .deb

sudo dpkg -i mytool_1.2.3_amd64.deb

winget (Windows Package Manager)

Manifest YAML Schema

winget manifests consist of multiple YAML files in a versioned directory structure within the microsoft/winget-pkgs repository.

Directory structure:

manifests/ m/ MyOrg/ MyTool/ 1.2.3/ MyOrg.MyTool.yaml # Version manifest MyOrg.MyTool.installer.yaml # Installer manifest MyOrg.MyTool.locale.en-US.yaml # Locale manifest

Version manifest (MyOrg.MyTool.yaml):

PackageIdentifier: MyOrg.MyTool PackageVersion: 1.2.3 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.9.0

Installer manifest (MyOrg.MyTool.installer.yaml):

PackageIdentifier: MyOrg.MyTool PackageVersion: 1.2.3 InstallerType: zip NestedInstallerType: portable NestedInstallerFiles:

Locale manifest (MyOrg.MyTool.locale.en-US.yaml):

PackageIdentifier: MyOrg.MyTool PackageVersion: 1.2.3 PackageLocale: en-US PackageName: MyTool Publisher: My Org ShortDescription: A CLI tool for managing widgets License: MIT PackageUrl: https://github.com/myorg/mytool ManifestType: defaultLocale ManifestVersion: 1.9.0

Submitting to winget-pkgs

  • Fork microsoft/winget-pkgs on GitHub

  • Create the manifest files in the correct directory structure

  • Validate locally: winget validate --manifest <path>

  • Submit a PR -- automated checks run against the manifest

  • Microsoft team reviews and merges

See [skill:dotnet-cli-release-pipeline] for automating winget PR creation.

Scoop (Windows)

Scoop is popular among Windows power users. Manifests are JSON files in a bucket repository.

Scoop Manifest

{ "version": "1.2.3", "description": "A CLI tool for managing widgets", "homepage": "https://github.com/myorg/mytool", "license": "MIT", "architecture": { "64bit": { "url": "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-win-x64.zip", "hash": "abc123..." }, "arm64": { "url": "https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-win-arm64.zip", "hash": "def456..." } }, "bin": "mytool.exe", "checkver": { "github": "https://github.com/myorg/mytool" }, "autoupdate": { "architecture": { "64bit": { "url": "https://github.com/myorg/mytool/releases/download/v$version/mytool-$version-win-x64.zip" }, "arm64": { "url": "https://github.com/myorg/mytool/releases/download/v$version/mytool-$version-win-arm64.zip" } } } }

Hosting a Scoop Bucket

Create a GitHub repo named scoop-mytool (or scoop-bucket ):

myorg/scoop-mytool/ bucket/ mytool.json

Users install with:

scoop bucket add myorg https://github.com/myorg/scoop-mytool scoop install mytool

Chocolatey

Chocolatey is Windows' most established package manager for binary distribution.

Package Structure

mytool/ mytool.nuspec tools/ chocolateyInstall.ps1 LICENSE.txt

mytool.nuspec:

<?xml version="1.0" encoding="utf-8"?> <package xmlns="http://schemas.xmldata.org/2004/07/nuspec"> <metadata> <id>mytool</id> <version>1.2.3</version> <title>MyTool</title> <authors>My Org</authors> <projectUrl>https://github.com/myorg/mytool&#x3C;/projectUrl> <license type="expression">MIT</license> <description>A CLI tool for managing widgets.</description> <tags>cli dotnet tools</tags> </metadata> </package>

tools/chocolateyInstall.ps1:

$ErrorActionPreference = 'Stop'

$packageArgs = @{ packageName = 'mytool' url64bit = 'https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-win-x64.zip' checksum64 = 'ABC123...' checksumType64 = 'sha256' unzipLocation = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)" }

Install-ChocolateyZipPackage @packageArgs

Building and Publishing

Pack the .nupkg

choco pack mytool.nuspec

Test locally

choco install mytool --source="." --force

Push to Chocolatey Community Repository

choco push mytool.1.2.3.nupkg --source https://push.chocolatey.org/ --api-key $env:CHOCO_API_KEY

dotnet tool (Global and Local)

dotnet tool is the simplest distribution for .NET developers. Tools are distributed as NuGet packages.

Project Configuration for Tool Packaging

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework>

&#x3C;!-- Tool packaging properties -->
&#x3C;PackAsTool>true&#x3C;/PackAsTool>
&#x3C;ToolCommandName>mytool&#x3C;/ToolCommandName>
&#x3C;PackageId>MyOrg.MyTool&#x3C;/PackageId>
&#x3C;Version>1.2.3&#x3C;/Version>
&#x3C;Description>A CLI tool for managing widgets&#x3C;/Description>
&#x3C;Authors>My Org&#x3C;/Authors>
&#x3C;PackageLicenseExpression>MIT&#x3C;/PackageLicenseExpression>
&#x3C;PackageProjectUrl>https://github.com/myorg/mytool&#x3C;/PackageProjectUrl>
&#x3C;PackageReadmeFile>README.md&#x3C;/PackageReadmeFile>

</PropertyGroup>

<ItemGroup> <None Include="../../README.md" Pack="true" PackagePath="/" /> </ItemGroup> </Project>

Building and Publishing

Pack the tool

dotnet pack -c Release

Publish to NuGet.org

dotnet nuget push bin/Release/MyOrg.MyTool.1.2.3.nupkg
--source https://api.nuget.org/v3/index.json
--api-key "$NUGET_API_KEY"

Installing dotnet Tools

Global tool (available system-wide)

dotnet tool install -g MyOrg.MyTool

Local tool (per-project, tracked in .config/dotnet-tools.json)

dotnet new tool-manifest # first time only dotnet tool install MyOrg.MyTool

Update

dotnet tool update -g MyOrg.MyTool

Run local tool

dotnet tool run mytool

or just:

dotnet mytool

Global vs Local Tools

Aspect Global Tool Local Tool

Scope System-wide (per user) Per-project directory

Install location ~/.dotnet/tools

.config/dotnet-tools.json

Version management Manual update Tracked in source control

CI/CD Must install before use dotnet tool restore restores all

Best for Personal productivity tools Project-specific build tools

NuGet Distribution

For tools distributed as NuGet packages (either as dotnet tool or standalone):

Package Metadata

<PropertyGroup> <PackageId>MyOrg.MyTool</PackageId> <Version>1.2.3</Version> <Description>A CLI tool for managing widgets</Description> <Authors>My Org</Authors> <PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageProjectUrl>https://github.com/myorg/mytool&#x3C;/PackageProjectUrl> <PackageReadmeFile>README.md</PackageReadmeFile> <PackageTags>cli;tools;widgets</PackageTags> <RepositoryUrl>https://github.com/myorg/mytool&#x3C;/RepositoryUrl> <RepositoryType>git</RepositoryType> </PropertyGroup>

Publishing to NuGet.org

Pack

dotnet pack -c Release -o ./nupkgs

Push (use env var for API key -- never hardcode)

dotnet nuget push ./nupkgs/MyOrg.MyTool.1.2.3.nupkg
--source https://api.nuget.org/v3/index.json
--api-key "$NUGET_API_KEY"

Private Feed Distribution

Push to a private feed (Azure Artifacts, GitHub Packages, etc.)

dotnet nuget push ./nupkgs/MyOrg.MyTool.1.2.3.nupkg
--source https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json
--api-key "$AZURE_ARTIFACTS_PAT"

Package Format Comparison

Format Platform Requires .NET Auto-Update Difficulty

Homebrew formula macOS, Linux No (binary tap) brew upgrade

Medium

apt/deb Debian/Ubuntu No (AOT binary) Via apt repo Medium

winget Windows 10+ No (portable) winget upgrade

Medium

Scoop Windows No (portable) scoop update

Low

Chocolatey Windows No choco upgrade

Medium

dotnet tool Cross-platform Yes (SDK) dotnet tool update

Low

NuGet (library) Cross-platform Yes (SDK) NuGet restore Low

Agent Gotchas

  • Do not hardcode SHA-256 hashes in package manifests. Generate checksums from actual release artifacts, not placeholder values. All package managers validate checksums against downloaded files.

  • Do not use InstallerType: exe for portable CLI tools in winget. Use InstallerType: zip with NestedInstallerType: portable for standalone executables. The exe type implies an installer with silent flags.

  • Do not forget PackAsTool for dotnet tool projects. Without <PackAsTool>true</PackAsTool> , dotnet pack produces a library package, not an installable tool.

  • Do not hardcode API keys in packaging scripts. Use environment variable references ($NUGET_API_KEY , $env:CHOCO_API_KEY ) with a comment noting CI secret configuration.

  • Do not mix Homebrew formula and cask for the same CLI tool. Pure CLI tools should use formulae. Casks are for GUI applications with macOS app bundles.

  • Do not skip the test block in Homebrew formulae. Homebrew CI runs formula tests. A missing test block causes review rejection. At minimum, test --version output.

References

  • Homebrew Formula Cookbook

  • Homebrew Taps

  • dpkg-deb manual

  • winget manifest schema

  • winget-pkgs repository

  • Scoop Wiki

  • Chocolatey package creation

  • .NET tool packaging

  • NuGet publishing

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

dotnet-devops

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

dotnet-csharp-code-smells

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

dotnet-cli-distribution

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

dotnet-github-docs

No summary provided by upstream source.

Repository SourceNeeds Review