mcpField
D
dotnet-cli-packaging

dotnet-cli-packaging

github.com/wshaddix开发8671 次安装35 周前更新
代码仓库

关于 dotnet-cli-packaging

dotnet-cli-packaging 是面向 .NET 命令行工具维护者的打包技能,帮助准备分发包。它提供 Homebrew、apt/deb、winget、Scoop、Chocolatey、dotnet tool 和 NuGet 格式的指南与示例。

主要功能

  1. 01创建 Homebrew formula 和 cask
  2. 02用 `dpkg-deb` 构建 apt/deb 包
  3. 03准备 winget 和 Scoop 清单
  4. 04为 Chocolatey 打包工具
  5. 05配置 `dotnet tool` 包
  6. 06向 NuGet 发布包

适用场景

  1. 01为 Homebrew 打包 .NET 命令行工具
  2. 02准备 Debian 或 Ubuntu 的 `.deb` 发布包
  3. 03向 winget 或 Scoop 提交命令行工具包
  4. 04通过 dotnet tool 或 NuGet 分发工具

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-cli-packaging

安装后在 Claude Code 中输入/dotnet-cli-packaging确认已加载。

详细介绍

dotnet-cli-packaging

面向多平台的 .NET CLI 工具打包:Homebrew 公式编写(二进制 tap 和 cask)、使用 dpkg-deb 打包 apt/deb、winget 清单 YAML 架构及向 winget-pkgs 提交 PR、Scoop 清单 JSON、创建 Chocolatey 包、dotnet tool 全局/本地打包,以及 NuGet 分发。

**版本假设:**基准版本为 .NET 8.0+。各包管理器的格式在不同 .NET 版本间保持稳定。

**不在范围内:**CLI 分发策略(AOT、依赖框架运行或选择 dotnet tool)——参见 [skill:dotnet-cli-distribution]。自动化打包的发布 CI/CD 流水线——参见 [skill:dotnet-cli-release-pipeline]。Native AOT 编译——参见 [skill:dotnet-native-aot]。基于容器的分发——参见 [skill:dotnet-containers]。通用 CI/CD 模式——参见 [skill:dotnet-gha-patterns] 和 [skill:dotnet-ado-patterns]。

交叉引用:[skill:dotnet-cli-distribution] 介绍分发策略和 RID 矩阵,[skill:dotnet-cli-release-pipeline] 介绍自动发布软件包,[skill:dotnet-native-aot] 介绍 AOT 二进制文件生成,[skill:dotnet-containers] 介绍基于容器的分发,[skill:dotnet-tool-management] 介绍用户侧的工具安装和清单管理。


Homebrew(macOS / Linux)

Homebrew 是 macOS 的主要包管理器,在 Linux 上也被广泛使用。CLI 工具有两种分发格式。

二进制 Tap(公式)

公式会按平台下载预构建的二进制文件。这是 Native AOT CLI 工具的推荐方式。

# 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
托管 Tap

Tap 是包含公式的 Git 仓库。创建一个名为 homebrew-tap 的仓库:

myorg/homebrew-tap/
  Formula/
    mytool.rb

用户通过以下方式安装:

brew tap myorg/tap
brew install mytool
Homebrew Cask

Cask 适用于 GUI 应用或带安装程序的工具。对于纯 CLI 工具,优先使用公式,而不是 cask。

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

使用 dpkg-deb 构建 .deb 软件包

创建软件包目录结构:

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

Control 文件:

Package: mytool
Version: 1.2.3
Section: utils
Priority: optional
Architecture: amd64
Maintainer: My Org <[email protected]>
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

构建软件包:

#!/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 <[email protected]>
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 到 Debian 架构的映射:

.NET RIDDebian 架构
linux-x64amd64
linux-arm64arm64
安装 .deb
sudo dpkg -i mytool_1.2.3_amd64.deb

winget(Windows 包管理器)

清单 YAML 架构

winget 清单由多个 YAML 文件组成,存放在 microsoft/winget-pkgs 仓库的版本化目录结构中。

目录结构:

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

版本清单(MyOrg.MyTool.yaml):

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

安装程序清单(MyOrg.MyTool.installer.yaml):

PackageIdentifier: MyOrg.MyTool
PackageVersion: 1.2.3
InstallerType: zip
NestedInstallerType: portable
NestedInstallerFiles:
  - RelativeFilePath: mytool.exe
    PortableCommandAlias: mytool
Installers:
  - Architecture: x64
    InstallerUrl: https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-win-x64.zip
    InstallerSha256: ABC123...
  - Architecture: arm64
    InstallerUrl: https://github.com/myorg/mytool/releases/download/v1.2.3/mytool-1.2.3-win-arm64.zip
    InstallerSha256: DEF456...
ManifestType: installer
ManifestVersion: 1.9.0

区域设置清单(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
提交到 winget-pkgs
  1. 在 GitHub 上 Fork microsoft/winget-pkgs
  2. 在正确的目录结构中创建清单文件
  3. 在本地验证:winget validate --manifest <path>
  4. 提交 PR——系统会针对清单运行自动检查
  5. Microsoft 团队审核并合并

参见 [skill:dotnet-cli-release-pipeline],了解如何自动创建 winget PR。


Scoop(Windows)

Scoop 在 Windows 高级用户中很受欢迎。清单是存放在 bucket 仓库中的 JSON 文件。

Scoop 清单
{
  "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"
      }
    }
  }
}
托管 Scoop Bucket

创建一个名为 scoop-mytool(或 scoop-bucket)的 GitHub 仓库:

myorg/scoop-mytool/
  bucket/
    mytool.json

用户通过以下方式安装:

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

Chocolatey

Chocolatey 是 Windows 上历史最悠久的二进制分发包管理器。

软件包结构
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</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
构建和发布
# 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(全局和本地)

对于 .NET 开发者来说,dotnet tool 是最简单的分发方式。工具以 NuGet 软件包的形式分发。

工具打包的项目配置
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>

    
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>mytool</ToolCommandName>
    <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</PackageProjectUrl>
    <PackageReadmeFile>README.md</PackageReadmeFile>
  </PropertyGroup>

  <ItemGroup>
    <None Include="../../README.md" Pack="true" PackagePath="/" />
  </ItemGroup>
</Project>
构建和发布
# 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"
安装 dotnet 工具
# 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
全局工具与本地工具
方面全局工具本地工具
范围系统范围(按用户)按项目目录
安装位置~/.dotnet/tools.config/dotnet-tools.json
版本管理手动更新在源代码管理中跟踪
CI/CD使用前必须安装dotnet tool restore 会还原所有工具
最适用于个人效率工具项目专用构建工具

NuGet 分发

对于通过 NuGet 包分发的工具(无论是作为 dotnet tool 还是独立工具):

包元数据
<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</PackageProjectUrl>
  <PackageReadmeFile>README.md</PackageReadmeFile>
  <PackageTags>cli;tools;widgets</PackageTags>
  <RepositoryUrl>https://github.com/myorg/mytool</RepositoryUrl>
  <RepositoryType>git</RepositoryType>
</PropertyGroup>
发布到 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"
私有源分发
# 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"

包格式比较

格式平台是否需要 .NET自动更新难度
Homebrew formulamacOS、Linux否(二进制 tap)brew upgrade中
apt/debDebian/Ubuntu否(AOT 二进制)通过 apt 仓库中
wingetWindows 10+否(便携式)winget upgrade中
ScoopWindows否(便携式)scoop update低
ChocolateyWindows否choco upgrade中
dotnet tool跨平台是(SDK)dotnet tool update低
NuGet(库)跨平台是(SDK)NuGet 还原低

Agent 常见问题

  1. 不要在包清单中硬编码 SHA-256 哈希值。 根据实际发布产物生成校验和,而不是使用占位值。所有包管理器都会验证下载文件的校验和。
  2. 不要在 winget 中为便携式 CLI 工具使用 InstallerType: exe。 独立可执行文件应使用 InstallerType: zip 和 NestedInstallerType: portable。exe 类型表示带有静默标志的安装程序。
  3. dotnet tool 项目不要漏掉 PackAsTool。 如果没有 <PackAsTool>true</PackAsTool>,dotnet pack 生成的是库包,而不是可安装的工具。
  4. 不要在打包脚本中硬编码 API 密钥。 使用环境变量引用($NUGET_API_KEY、$env:CHOCO_API_KEY),并添加注释说明 CI 密钥配置。
  5. 不要为同一个 CLI 工具同时使用 Homebrew formula 和 cask。 纯 CLI 工具应使用 formula。Cask 用于带有 macOS 应用包的 GUI 应用程序。
  6. 不要跳过 Homebrew formula 中的 test 块。 Homebrew CI 会运行 formula 测试。缺少测试块会导致审核被拒。至少应测试 --version 输出。

参考资料

评论

支持 Markdown