In distributed enterprise software engineering, developer tooling (bitz) is not merely a project generator; it acts as the primary gateway for cross-platform diagnostics, hardware licensing handshakes, OpenAPI-driven proxy synthesis, and CQRS vertical slice scaffolding.
Distributing CLI binaries via ad-hoc scripts or requiring engineers to compile from source inevitably causes severe version fragmentation and risks leaking commercial licensing private keys and proprietary business models.
Dual-Track Distribution Topology
BitzOrcas.Modern implements a .NET Global Tool dual-track topology: public open-source scaffolding is distributed via NuGet.org for the broader community, while commercial enterprise capabilities (including LegalTech domain generators, hardware licensing locks, and SSO integration) are securely hosted on Alibaba Cloud Yunxiao Private NuGet Feeds.
Architectural Trade-offs
When designing tool distribution, software architects must evaluate three core trade-offs:
| Criterion | Ad-hoc Shell Script / Local Source Build | Single Public Feed Only | BitzOrcas Dual-Track Strategy (Selected) |
|---|---|---|---|
| Version Consistency | Very poor; divergent build configurations | High; driven uniformly by NuGet versions | Strict; enforced via CPM and semantic versions |
| IP Protection | High risk; licensing keys exposed in source | Non-existent; commercial modules forced open | Isolated; commercial binaries restricted to private feed |
| Supply Chain Security | Fragile; prone to local workstation tampering | Vulnerable to Dependency Confusion attacks | Zero-Trust; strictly governed by packageSourceMapping |
Step 1: Tool Packaging Contract (BitzOrcas.Cli.csproj)
The CLI project must be configured as a standard .NET Global Tool:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <!-- Output must be an executable binary --> <OutputType>Exe</OutputType> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings>
<!-- Global tool packaging properties --> <PackAsTool>true</PackAsTool> <!-- Command registered in global system PATH --> <ToolCommandName>bitz</ToolCommandName> <PackageOutputPath>$(MSBuildThisFileDirectory)../../artifacts/packages</PackageOutputPath>
<!-- Semantic package identity --> <PackageId>BitzOrcas.Cli</PackageId> <Title>Bitz CLI Developer Toolchain</Title> <Authors>BitzOrcas Core Architecture Team</Authors> <Company>LinkedLaw BitzSoft</Company> <Description>Developer toolchain, vertical slice scaffolding, and licensing manager for BitzOrcas.Modern.</Description> <PackageTags>dotnet-tool;bitz;architecture;scaffolding;cqrs</PackageTags> <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> </PropertyGroup>
</Project>Step 2: Local Packaging & Offline Validation
Before pushing packages to any remote registry, release managers must validate the package locally in an isolated test environment:
# 1. Clean previous build artifactsdotnet clean src/tooling/BitzOrcas.Cli/BitzOrcas.Cli.csproj -c Release
# 2. Pack release package and symbol bundledotnet pack src/tooling/BitzOrcas.Cli/BitzOrcas.Cli.csproj \ -c Release \ -p:Version=2.5.0 \ -p:IncludeSymbols=true \ -p:SymbolPackageFormat=snupkg \ -o ./artifacts/packages
# 3. Uninstall existing global tool to prevent cache interferencedotnet tool uninstall -g BitzOrcas.Cli || true
# 4. Install directly from local staging directorydotnet tool install -g BitzOrcas.Cli \ --add-source ./artifacts/packages \ --version 2.5.0
# 5. Verify runtime health and version reportbitz --versionbitz doctor --verboseStep 3: Pushing to Enterprise Private Feed (Yunxiao)
Commercial editions (such as BitzOrcas.Cli.Enterprise) containing proprietary LegalTech generators and hardware licensing routines are pushed to Alibaba Cloud Yunxiao:
# Export credentials (never hardcode in repository scripts)export BITZORCAS_COMMERCIAL_FEED_URL="https://packages.aliyun.com/63a120000000000000/nuget/v3/index.json"export BITZORCAS_COMMERCIAL_PUSH_TOKEN="sec_pat_xxxxxxxxxxxxxxxxxxxx"
# Push commercial package to private feeddotnet nuget push ./artifacts/packages/BitzOrcas.Cli.Enterprise.2.5.0.nupkg \ --source "${BITZORCAS_COMMERCIAL_FEED_URL}" \ --api-key "${BITZORCAS_COMMERCIAL_PUSH_TOKEN}" \ --skip-duplicateSecurity Rule: The private registry must enforce package immutability. If a bug is discovered after release, increment the patch version (
2.5.1) instead of overwriting the package.
Step 4: Publishing to Public NuGet.org
Pushing public community tools to NuGet.org requires an enterprise API key managed securely via KMS:
# Push community edition to official NuGet.orgdotnet nuget push ./artifacts/packages/BitzOrcas.Cli.2.5.0.nupkg \ --source https://api.nuget.org/v3/index.json \ --api-key "${NUGET_ORG_API_KEY}" \ --skip-duplicateStep 5: Supply Chain Defense (packageSourceMapping)
To prevent Dependency Confusion attacks, every downstream business repository must configure packageSourceMapping in nuget.config:
<?xml version="1.0" encoding="utf-8"?><configuration> <packageSources> <clear /> <!-- Public Official Source --> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <!-- Enterprise Commercial Source --> <add key="BitzOrcasCommercial" value="%BITZORCAS_COMMERCIAL_FEED_URL%" /> </packageSources>
<!-- Strict namespace isolation to prevent package spoofing --> <packageSourceMapping> <!-- Public packages can only be resolved from nuget.org --> <packageSource key="nuget.org"> <package pattern="*" /> <package pattern="BitzOrcas.Cli" /> <package pattern="Microsoft.*" /> <package pattern="System.*" /> </packageSource>
<!-- Commercial proprietary packages are restricted to private feed --> <packageSource key="BitzOrcasCommercial"> <package pattern="BitzOrcas.*" /> <package pattern="BitzOrcas.Cli.Enterprise" /> <package pattern="Bitzsoft.Licensing.*" /> </packageSource> </packageSourceMapping></configuration>Step 6: Automated CI/CD Workflow
Deploy dual-track releases automatically upon Git tag pushes:
name: Publish Bitz CLI Global Tool
on: push: tags: - 'v*.*.*'
jobs: build-and-publish: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4
- name: Setup .NET 10 SDK uses: actions/setup-dotnet@v4 with: dotnet-version: '10.0.x'
- name: Pack CLI run: | VERSION=${GITHUB_REF_NAME#v} dotnet pack src/tooling/BitzOrcas.Cli/BitzOrcas.Cli.csproj \ -c Release \ -p:Version=${VERSION} \ -o ./artifacts/packages
- name: Push to Enterprise Yunxiao Feed env: FEED_URL: ${{ secrets.BITZORCAS_COMMERCIAL_FEED_URL }} FEED_TOKEN: ${{ secrets.BITZORCAS_COMMERCIAL_PUSH_TOKEN }} run: | dotnet nuget push ./artifacts/packages/*.nupkg \ --source "${FEED_URL}" \ --api-key "${FEED_TOKEN}" \ --skip-duplicate
- name: Push to NuGet.org (Public Track) if: "!contains(github.ref, '-rc') && !contains(github.ref, '-beta')" env: NUGET_KEY: ${{ secrets.NUGET_ORG_API_KEY }} run: | dotnet nuget push ./artifacts/packages/BitzOrcas.Cli.*.nupkg \ --source https://api.nuget.org/v3/index.json \ --api-key "${NUGET_KEY}" \ --skip-duplicateTroubleshooting Guide
| Symptom | Root Cause | Remediation |
|---|---|---|
NU1100: Unable to resolve package 'Bitz.Cli' | Missing pattern in packageSourceMapping | Run bitz doctor --fix or verify nuget.config pattern mapping |
HTTP 409 Conflict: Package already exists | Immutability protection triggered on registry | Increment the patch version (2.5.1) and re-pack |
HTTP 401 Unauthorized | Expired feed token or invalid API key | Regenerate access token in registry settings and update CI secrets |
bitz: command not found | PATH variable missing .NET tools directory | Add export PATH="$PATH:$HOME/.dotnet/tools" to shell profile |