|
| 1 | +# NuGet Package Management: Preview and Stable Builds |
| 2 | + |
| 3 | +This document outlines the best practices for managing preview and stable NuGet package builds in the Codebreaker Backend solution, including GitHub Actions workflows and deployment strategies. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The Codebreaker Backend solution uses a dual-pipeline approach for NuGet package management: |
| 8 | + |
| 9 | +- **Preview Builds**: Automatically triggered on code changes, deployed to Azure DevOps Artifacts |
| 10 | +- **Stable Builds**: Manually triggered, deployed to both Azure DevOps Artifacts and public NuGet Gallery |
| 11 | + |
| 12 | +## Architecture |
| 13 | + |
| 14 | +``` |
| 15 | +Code Changes (main branch) |
| 16 | + ↓ |
| 17 | +Preview Build Pipeline |
| 18 | + ↓ (automatic) |
| 19 | +Azure DevOps Artifacts (preview) |
| 20 | + ↓ (manual approval) |
| 21 | +Stable Build Pipeline |
| 22 | + ↓ (automatic) |
| 23 | +Azure DevOps Artifacts (stable) + NuGet Gallery |
| 24 | +``` |
| 25 | + |
| 26 | +## Package Versioning Strategy |
| 27 | + |
| 28 | +### Preview Builds |
| 29 | +- **Format**: `{base-version}-preview.1.{build-number + offset}` |
| 30 | +- **Example**: `3.8.0-preview.1.15` (if build number is 5 and offset is 10) |
| 31 | +- **Trigger**: Automatic on push to main branch with changes in specific paths |
| 32 | +- **Retention**: 3 days |
| 33 | + |
| 34 | +### Stable Builds |
| 35 | +- **Format**: `{base-version}` (no suffix) |
| 36 | +- **Example**: `3.8.0` |
| 37 | +- **Trigger**: Manual workflow dispatch |
| 38 | +- **Retention**: 30 days |
| 39 | + |
| 40 | +## Central Package Management |
| 41 | + |
| 42 | +The solution uses Central Package Management with `Directory.Packages.props`: |
| 43 | + |
| 44 | +```xml |
| 45 | +<PropertyGroup> |
| 46 | + <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> |
| 47 | + <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled> |
| 48 | +</PropertyGroup> |
| 49 | +``` |
| 50 | + |
| 51 | +### Key Points: |
| 52 | +- ✅ **DO**: Add package versions to `src/Directory.Packages.props` |
| 53 | +- ❌ **DON'T**: Specify versions in individual `.csproj` files |
| 54 | +- ⚠️ **Warning**: Build will fail if PackageReference has version but package not in Directory.Packages.props |
| 55 | + |
| 56 | +## GitHub Actions Workflows |
| 57 | + |
| 58 | +### 1. Preview Build Workflows |
| 59 | + |
| 60 | +Each library has a dedicated preview workflow (e.g., `codebreaker-lib-sqlserver.yml`): |
| 61 | + |
| 62 | +```yaml |
| 63 | +name: Sql Server data lib |
| 64 | + |
| 65 | +on: |
| 66 | + push: |
| 67 | + branches: [ main ] |
| 68 | + paths: |
| 69 | + - 'src/services/common/Codebreaker.Data.SqlServer/**' |
| 70 | + workflow_dispatch: |
| 71 | + |
| 72 | +jobs: |
| 73 | + build: |
| 74 | + uses: CodebreakerApp/Codebreaker.Backend/.github/workflows/createnuget-withbuildnumber.yml@main |
| 75 | + with: |
| 76 | + version-suffix: preview.1. |
| 77 | + version-number: ${{ github.run_number }} |
| 78 | + version-offset: 10 |
| 79 | + solutionfile-path: src/Codebreaker.Backend.SqlServer.slnx |
| 80 | + projectfile-path: src/services/common/Codebreaker.Data.SqlServer/Codebreaker.Data.SqlServer.csproj |
| 81 | + dotnet-version: '9.0.x' |
| 82 | + artifact-name: codebreaker-sqlserver |
| 83 | + branch-name: main |
| 84 | + |
| 85 | + publishdevops: |
| 86 | + uses: CodebreakerApp/Codebreaker.Backend/.github/workflows/publishnuget-azuredevops.yml@main |
| 87 | + needs: build |
| 88 | + with: |
| 89 | + artifact-name: codebreaker-sqlserver |
| 90 | + secrets: inherit |
| 91 | +``` |
| 92 | +
|
| 93 | +### 2. Stable Build Workflows |
| 94 | +
|
| 95 | +Each library has a stable workflow (e.g., `codebreaker-lib-sqlserver-stable.yml`): |
| 96 | + |
| 97 | +```yaml |
| 98 | +name: SqlServer stable lib |
| 99 | +
|
| 100 | +on: |
| 101 | + workflow_dispatch: |
| 102 | +
|
| 103 | +jobs: |
| 104 | + build: |
| 105 | + runs-on: ubuntu-latest |
| 106 | + steps: |
| 107 | + - name: Checkout to the branch |
| 108 | + uses: actions/checkout@v5 |
| 109 | + with: |
| 110 | + ref: main |
| 111 | +
|
| 112 | + - name: Setup .NET |
| 113 | + uses: actions/setup-dotnet@v5 |
| 114 | + with: |
| 115 | + dotnet-version: 9.0.x |
| 116 | +
|
| 117 | + - name: Build the library |
| 118 | + run: dotnet build -c Release ${{ env.solutionfile-path }} |
| 119 | +
|
| 120 | + - name: Run the unit tests |
| 121 | + run: dotnet test ${{ env.solutionfile-path }} |
| 122 | + |
| 123 | + - name: Create a Package |
| 124 | + run: dotnet pack -c Release ${{ env.projectfile-path }} -o packages |
| 125 | +
|
| 126 | + publishdevops: |
| 127 | + uses: CodebreakerApp/Codebreaker.Backend/.github/workflows/publishnuget-azuredevops.yml@main |
| 128 | + needs: build |
| 129 | + with: |
| 130 | + artifact-name: codebreaker-sqlserver-stable |
| 131 | + secrets: inherit |
| 132 | +
|
| 133 | + publishnuget: |
| 134 | + uses: CodebreakerApp/Codebreaker.Backend/.github/workflows/publishnuget-nugetserver.yml@main |
| 135 | + needs: publishdevops |
| 136 | + with: |
| 137 | + artifact-name: codebreaker-sqlserver-stable |
| 138 | + secrets: inherit |
| 139 | +``` |
| 140 | + |
| 141 | +### 3. Reusable Workflows |
| 142 | + |
| 143 | +#### Build Workflow (`createnuget-withbuildnumber.yml`) |
| 144 | + |
| 145 | +Features: |
| 146 | +- **Deterministic builds** for reproducible packages |
| 147 | +- **Source linking** with embedded sources |
| 148 | +- **Symbol packages** (.snupkg) generation |
| 149 | +- **Version calculation** with offsets |
| 150 | +- **Multi-targeting** support (net8.0;net9.0) |
| 151 | + |
| 152 | +Key parameters: |
| 153 | +```yaml |
| 154 | +inputs: |
| 155 | + version-suffix: preview.1. # Quality marker |
| 156 | + version-number: ${{ github.run_number }} # Build number |
| 157 | + version-offset: 10 # Offset for version numbering |
| 158 | + solutionfile-path: src/Codebreaker.Backend.SqlServer.slnx |
| 159 | + projectfile-path: src/services/common/Codebreaker.Data.SqlServer/Codebreaker.Data.SqlServer.csproj |
| 160 | + dotnet-version: '9.0.x' |
| 161 | + artifact-name: codebreaker-sqlserver |
| 162 | +``` |
| 163 | + |
| 164 | +Deterministic build settings: |
| 165 | +```bash |
| 166 | +/p:ContinuousIntegrationBuild=true |
| 167 | +/p:Deterministic=true |
| 168 | +/p:EmbedUntrackedSources=true |
| 169 | +/p:DebugType=embedded |
| 170 | +/p:PublishRepositoryUrl=true |
| 171 | +/p:PathMap='$(MSBuildProjectDirectory)=/' |
| 172 | +``` |
| 173 | + |
| 174 | +#### Azure DevOps Publish Workflow (`publishnuget-azuredevops.yml`) |
| 175 | + |
| 176 | +```yaml |
| 177 | +env: |
| 178 | + ARTIFACTS_URL: "https://pkgs.dev.azure.com/cnilearn/codebreakerpackages/_packaging/codebreaker/nuget/v3/index.json" |
| 179 | +
|
| 180 | +steps: |
| 181 | + - name: Add the Azure DevOps Artifacts Package Source |
| 182 | + run: dotnet nuget add source --username USERNAME --password ${{ secrets.DEVOPSARTIFACT_PAT }} --store-password-in-clear-text --name devopscninnovation ${{ env.ARTIFACTS_URL }} |
| 183 | + |
| 184 | + - name: Publish to Azure DevOps Artifacts |
| 185 | + run: dotnet nuget push "packages/*.nupkg" --api-key ${{ secrets.DEVOPSARTIFACT_PAT }} --source devopscninnovation --skip-duplicate |
| 186 | +``` |
| 187 | + |
| 188 | +#### NuGet Gallery Publish Workflow (`publishnuget-nugetserver.yml`) |
| 189 | + |
| 190 | +```yaml |
| 191 | +env: |
| 192 | + ARTIFACTS_URL: https://api.nuget.org/v3/index.json |
| 193 | +
|
| 194 | +steps: |
| 195 | + - name: Publish to the NuGet server (nupkg and snupkg) |
| 196 | + run: dotnet nuget push "packages/*.nupkg" --api-key ${{ secrets.NUGETAPIKEY }} --source ${{ env.ARTIFACTS_URL }} |
| 197 | +``` |
| 198 | + |
| 199 | +## Deployment Targets |
| 200 | + |
| 201 | +### Azure DevOps Artifacts |
| 202 | +- **Purpose**: Internal package repository for preview and stable builds |
| 203 | +- **URL**: `https://pkgs.dev.azure.com/cnilearn/codebreakerpackages/_packaging/codebreaker/nuget/v3/index.json` |
| 204 | +- **Authentication**: Personal Access Token (PAT) |
| 205 | +- **Environment**: `DevOpsArtifacts` |
| 206 | +- **Features**: |
| 207 | + - Supports both preview and stable packages |
| 208 | + - Symbol packages (.snupkg) |
| 209 | + - Package retention policies |
| 210 | + |
| 211 | +### NuGet Gallery |
| 212 | +- **Purpose**: Public package repository for stable builds only |
| 213 | +- **URL**: `https://api.nuget.org/v3/index.json` |
| 214 | +- **Authentication**: API Key |
| 215 | +- **Environment**: `NugetServer` |
| 216 | +- **Features**: |
| 217 | + - Global package distribution |
| 218 | + - Automatic symbol package handling |
| 219 | + - Package signing validation |
| 220 | + |
| 221 | +## Required Secrets |
| 222 | + |
| 223 | +Configure these secrets in your GitHub repository: |
| 224 | + |
| 225 | +| Secret Name | Environment | Description | |
| 226 | +|-------------|-------------|-------------| |
| 227 | +| `DEVOPSARTIFACT_PAT` | DevOpsArtifacts | Personal Access Token for Azure DevOps Artifacts | |
| 228 | +| `NUGETAPIKEY` | NugetServer | API Key for NuGet Gallery | |
| 229 | + |
| 230 | +## Best Practices |
| 231 | + |
| 232 | +### 1. Version Management |
| 233 | +- Always update the base version in `.csproj` files before creating stable releases |
| 234 | +- Use semantic versioning (Major.Minor.Patch) |
| 235 | +- Preview versions automatically increment with build numbers |
| 236 | + |
| 237 | +### 2. Package Dependencies |
| 238 | +- Keep internal package versions synchronized in `Directory.Packages.props` |
| 239 | +- Use conditional package versions for multi-targeting: |
| 240 | + ```xml |
| 241 | + <PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" Condition="'$(TargetFramework)' == 'net8.0'" /> |
| 242 | + <PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" Condition="'$(TargetFramework)' == 'net9.0'" /> |
| 243 | + ``` |
| 244 | + |
| 245 | +### 3. Testing Strategy |
| 246 | +- All builds run unit tests before packaging |
| 247 | +- Preview builds enable faster feedback cycles |
| 248 | +- Stable builds have additional validation through Azure DevOps Artifacts before NuGet Gallery |
| 249 | + |
| 250 | +### 4. Path-Based Triggers |
| 251 | +- Preview workflows use path filters to trigger only on relevant changes: |
| 252 | + ```yaml |
| 253 | + paths: |
| 254 | + - 'src/services/common/Codebreaker.Data.SqlServer/**' |
| 255 | + ``` |
| 256 | + |
| 257 | +### 5. Artifact Management |
| 258 | +- Preview artifacts: 3-day retention |
| 259 | +- Stable artifacts: 30-day retention |
| 260 | +- Unique artifact names prevent conflicts |
| 261 | + |
| 262 | +## Workflow Execution Guide |
| 263 | + |
| 264 | +### Triggering Preview Builds |
| 265 | +1. **Automatic**: Push changes to main branch in monitored paths |
| 266 | +2. **Manual**: Use "Run workflow" button on GitHub Actions tab |
| 267 | + |
| 268 | +### Triggering Stable Builds |
| 269 | +1. Navigate to GitHub Actions → Select stable workflow (e.g., "SqlServer stable lib") |
| 270 | +2. Click "Run workflow" button |
| 271 | +3. Confirm deployment to production environments |
| 272 | + |
| 273 | +### Monitoring Builds |
| 274 | +- Check GitHub Actions tab for build status |
| 275 | +- Review Azure DevOps Artifacts for package availability |
| 276 | +- Verify NuGet Gallery for stable package publication |
| 277 | + |
| 278 | +## Troubleshooting |
| 279 | + |
| 280 | +### Common Issues |
| 281 | + |
| 282 | +#### Build Failures |
| 283 | +- **Deterministic build issues**: Ensure all source files are committed |
| 284 | +- **Package reference errors**: Verify `Directory.Packages.props` contains all required packages |
| 285 | +- **Multi-targeting issues**: Check conditional package references |
| 286 | + |
| 287 | +#### Deployment Failures |
| 288 | +- **Azure DevOps authentication**: Verify PAT token permissions |
| 289 | +- **NuGet Gallery publishing**: Check API key validity and package metadata |
| 290 | +- **Duplicate packages**: Use `--skip-duplicate` flag (already configured) |
| 291 | + |
| 292 | +#### Version Conflicts |
| 293 | +- **Preview version conflicts**: Increase version offset in workflow |
| 294 | +- **Stable version conflicts**: Update base version in project file |
| 295 | + |
| 296 | +### Debug Commands |
| 297 | + |
| 298 | +```bash |
| 299 | +# Local package creation (preview) |
| 300 | +dotnet pack --version-suffix preview.1.123 -c Release src/services/common/Codebreaker.Data.SqlServer/Codebreaker.Data.SqlServer.csproj |
| 301 | +
|
| 302 | +# Local package creation (stable) |
| 303 | +dotnet pack -c Release src/services/common/Codebreaker.Data.SqlServer/Codebreaker.Data.SqlServer.csproj |
| 304 | +
|
| 305 | +# Test package installation |
| 306 | +dotnet add package CNinnovation.Codebreaker.SqlServer --version 3.8.0-preview.1.123 --source https://pkgs.dev.azure.com/... |
| 307 | +``` |
| 308 | + |
| 309 | +## Integration with Development Workflow |
| 310 | + |
| 311 | +### For Library Developers |
| 312 | +1. **Development**: Work on feature branches |
| 313 | +2. **Integration**: Merge to main branch → Automatic preview build |
| 314 | +3. **Testing**: Use preview packages in dependent projects |
| 315 | +4. **Release**: Trigger stable build when ready for production |
| 316 | + |
| 317 | +### For Library Consumers |
| 318 | +1. **Development**: Use preview packages from Azure DevOps Artifacts |
| 319 | +2. **Production**: Use stable packages from NuGet Gallery |
| 320 | +3. **Testing**: Pin specific preview versions for reproducible builds |
| 321 | + |
| 322 | +## Future Enhancements |
| 323 | + |
| 324 | +### Planned Improvements |
| 325 | +- **Automated release notes** generation from Git history |
| 326 | +- **Package vulnerability scanning** integration |
| 327 | +- **Performance regression testing** for library updates |
| 328 | +- **Automated dependency updates** with Dependabot |
| 329 | +- **Package usage analytics** and deprecation warnings |
| 330 | + |
| 331 | +### Environment-Specific Deployments |
| 332 | +- **Development Environment**: Auto-deploy preview packages |
| 333 | +- **Staging Environment**: Manual promotion of stable candidates |
| 334 | +- **Production Environment**: Stable packages only with approval gates |
| 335 | + |
| 336 | +This documentation provides a comprehensive guide for managing NuGet packages in the Codebreaker Backend solution, ensuring reliable and efficient package distribution for both development and production scenarios. |
0 commit comments