|
| 1 | +# OneBranch DUMMY/TEST Release Pipeline for mssql-python |
| 2 | +# ⚠️ THIS IS A TEST PIPELINE - NOT FOR PRODUCTION RELEASES ⚠️ |
| 3 | +# Downloads wheel and symbol artifacts from build pipeline, publishes symbols, and performs dummy ESRP release for testing |
| 4 | +# Uses Maven ContentType instead of PyPI to avoid accidental production releases |
| 5 | +# This pipeline is ALWAYS Official - no NonOfficial option |
| 6 | + |
| 7 | +name: $(Year:YY)$(DayOfYear)$(Rev:.r)-Dummy-Release |
| 8 | + |
| 9 | +# Manual trigger only - releases should be deliberate |
| 10 | +trigger: none |
| 11 | +pr: none |
| 12 | + |
| 13 | +# Parameters for DUMMY release pipeline |
| 14 | +parameters: |
| 15 | + - name: packageVersion |
| 16 | + displayName: '[TEST] Package Version (e.g., 0.13.0)' |
| 17 | + type: string |
| 18 | + default: '0.13.0' |
| 19 | + |
| 20 | + - name: publishSymbols |
| 21 | + displayName: '[TEST] Publish Symbols to Symbol Servers' |
| 22 | + type: boolean |
| 23 | + default: true |
| 24 | + |
| 25 | + - name: performDummyRelease |
| 26 | + displayName: '[TEST] Perform Dummy ESRP Release (Maven - NOT PyPI)' |
| 27 | + type: boolean |
| 28 | + default: true # Safe to enable - uses Maven ContentType for testing |
| 29 | + |
| 30 | +# Variables |
| 31 | +variables: |
| 32 | + - name: PACKAGE_VERSION |
| 33 | + value: '${{ parameters.packageVersion }}' |
| 34 | + readonly: true |
| 35 | + |
| 36 | + - name: packageVersion |
| 37 | + value: '${{ parameters.packageVersion }}' |
| 38 | + readonly: true |
| 39 | + |
| 40 | + # Common variables |
| 41 | + - template: /OneBranchPipelines/variables/common-variables.yml@self |
| 42 | + - template: /OneBranchPipelines/variables/onebranch-variables.yml@self |
| 43 | + |
| 44 | + # Variable groups |
| 45 | + - group: 'ESRP Federated Creds (AME)' # Contains ESRP signing credentials |
| 46 | + - group: 'Symbols Publishing' # Contains SymbolServer, SymbolTokenUri variables |
| 47 | + |
| 48 | +# OneBranch resources |
| 49 | +resources: |
| 50 | + repositories: |
| 51 | + - repository: templates |
| 52 | + type: git |
| 53 | + name: 'OneBranch.Pipelines/GovernedTemplates' |
| 54 | + ref: 'refs/heads/main' |
| 55 | + |
| 56 | + # Reference to the build pipeline |
| 57 | + pipelines: |
| 58 | + - pipeline: buildPipeline |
| 59 | + source: 'Build-Release-Package-Pipeline' # Name of the build pipeline |
| 60 | + trigger: none # Manual trigger only |
| 61 | + |
| 62 | +# Extend OneBranch official template |
| 63 | +# Always uses Official template for release pipeline |
| 64 | +extends: |
| 65 | + template: 'v2/OneBranch.Official.CrossPlat.yml@templates' |
| 66 | + |
| 67 | + parameters: |
| 68 | + # Feature flags |
| 69 | + featureFlags: |
| 70 | + WindowsHostVersion: |
| 71 | + Version: '2022' |
| 72 | + |
| 73 | + # Global SDL Configuration |
| 74 | + globalSdl: |
| 75 | + # Minimal SDL for release pipeline - artifacts already scanned during build |
| 76 | + binskim: |
| 77 | + enabled: true |
| 78 | + break: true |
| 79 | + |
| 80 | + credscan: |
| 81 | + enabled: true |
| 82 | + suppressionsFile: '$(REPO_ROOT)/.config/CredScanSuppressions.json' |
| 83 | + |
| 84 | + policheck: |
| 85 | + enabled: true |
| 86 | + break: true |
| 87 | + exclusionFile: '$(REPO_ROOT)/.config/PolicheckExclusions.xml' |
| 88 | + |
| 89 | + # Publish SDL logs |
| 90 | + publishLogs: |
| 91 | + enabled: true |
| 92 | + |
| 93 | + # TSA - Always enabled for Official release pipeline |
| 94 | + tsa: |
| 95 | + enabled: true |
| 96 | + configFile: '$(REPO_ROOT)/.config/tsaoptions.json' |
| 97 | + |
| 98 | + # Pipeline stages |
| 99 | + stages: |
| 100 | + - stage: TestReleasePackages |
| 101 | + displayName: '[TEST] Dummy Release - Testing ESRP Workflow' |
| 102 | + |
| 103 | + jobs: |
| 104 | + - job: DownloadAndTestRelease |
| 105 | + displayName: '[TEST] Download Artifacts and Perform Dummy Release' |
| 106 | + |
| 107 | + pool: |
| 108 | + type: windows |
| 109 | + isCustom: true |
| 110 | + name: Django-1ES-pool |
| 111 | + vmImage: WIN22-SQL22 |
| 112 | + |
| 113 | + variables: |
| 114 | + ob_outputDirectory: '$(Build.ArtifactStagingDirectory)' |
| 115 | + |
| 116 | + steps: |
| 117 | + # Step 1: Download consolidated artifacts from build pipeline |
| 118 | + - task: DownloadPipelineArtifact@2 |
| 119 | + displayName: '[TEST] Download Consolidated Artifacts from Build Pipeline' |
| 120 | + inputs: |
| 121 | + buildType: 'specific' |
| 122 | + project: '$(System.TeamProject)' |
| 123 | + definition: 2199 # Build-Release-Package-Pipeline definition ID |
| 124 | + buildVersionToDownload: 'specific' |
| 125 | + buildId: $(resources.pipeline.buildPipeline.runID) # Use the build run selected in UI |
| 126 | + artifactName: 'drop_Consolidate_ConsolidateArtifacts' # Consolidated artifact with dist/ and symbols/ |
| 127 | + targetPath: '$(Build.SourcesDirectory)/artifacts' |
| 128 | + |
| 129 | + # Step 3: List downloaded artifacts for verification |
| 130 | + - task: PowerShell@2 |
| 131 | + displayName: '[TEST] List Downloaded Wheel and Symbol Files' |
| 132 | + inputs: |
| 133 | + targetType: 'inline' |
| 134 | + script: | |
| 135 | + Write-Host "=====================================" |
| 136 | + Write-Host "[TEST PIPELINE] Downloaded Artifacts:" |
| 137 | + Write-Host "=====================================" |
| 138 | + |
| 139 | + # List wheel files |
| 140 | + $wheelsPath = "$(Build.SourcesDirectory)/artifacts/dist" |
| 141 | + if (Test-Path $wheelsPath) { |
| 142 | + $wheels = Get-ChildItem -Path $wheelsPath -Filter "*.whl" -Recurse |
| 143 | + |
| 144 | + Write-Host "`n[WHEELS] Total wheel files found: $($wheels.Count)" |
| 145 | + foreach ($wheel in $wheels) { |
| 146 | + $size = [math]::Round($wheel.Length / 1MB, 2) |
| 147 | + Write-Host " - $($wheel.Name) (${size} MB)" |
| 148 | + } |
| 149 | + |
| 150 | + # Copy wheels to dist folder for ESRP |
| 151 | + Write-Host "`nCopying wheels to $(Build.SourcesDirectory)/dist..." |
| 152 | + New-Item -ItemType Directory -Force -Path "$(Build.SourcesDirectory)/dist" | Out-Null |
| 153 | + Copy-Item -Path "$wheelsPath/*.whl" -Destination "$(Build.SourcesDirectory)/dist/" -Force |
| 154 | + |
| 155 | + } else { |
| 156 | + Write-Error "Wheel directory not found at: $wheelsPath" |
| 157 | + exit 1 |
| 158 | + } |
| 159 | + |
| 160 | + # List symbol files |
| 161 | + $symbolsPath = "$(Build.SourcesDirectory)/artifacts/symbols" |
| 162 | + if (Test-Path $symbolsPath) { |
| 163 | + $symbols = Get-ChildItem -Path $symbolsPath -Filter "*.pdb" -Recurse |
| 164 | + |
| 165 | + Write-Host "`n[SYMBOLS] Total PDB files found: $($symbols.Count)" |
| 166 | + foreach ($symbol in $symbols) { |
| 167 | + $size = [math]::Round($symbol.Length / 1KB, 2) |
| 168 | + Write-Host " - $($symbol.Name) (${size} KB)" |
| 169 | + } |
| 170 | + |
| 171 | + # Copy symbols to symbols folder for publishing |
| 172 | + Write-Host "`nCopying symbols to $(Build.SourcesDirectory)/symbols..." |
| 173 | + New-Item -ItemType Directory -Force -Path "$(Build.SourcesDirectory)/symbols" | Out-Null |
| 174 | + Copy-Item -Path "$symbolsPath/*.pdb" -Destination "$(Build.SourcesDirectory)/symbols/" -Force |
| 175 | + |
| 176 | + } else { |
| 177 | + Write-Warning "Symbol directory not found at: $symbolsPath" |
| 178 | + Write-Warning "Symbol publishing will be skipped if no PDB files found" |
| 179 | + } |
| 180 | + |
| 181 | + Write-Host "`n=====================================" |
| 182 | + Write-Host "Summary:" |
| 183 | + Write-Host "Wheels: $($wheels.Count) files" |
| 184 | + Write-Host "Symbols: $(if ($symbols) { $symbols.Count } else { 0 }) files" |
| 185 | + Write-Host "=====================================" |
| 186 | + |
| 187 | + # Step 4: Verify wheel integrity |
| 188 | + - task: PowerShell@2 |
| 189 | + displayName: '[TEST] Verify Wheel Integrity' |
| 190 | + inputs: |
| 191 | + targetType: 'inline' |
| 192 | + script: | |
| 193 | + Write-Host "[TEST] Verifying wheel file integrity..." |
| 194 | + |
| 195 | + $wheels = Get-ChildItem -Path "$(Build.SourcesDirectory)/dist" -Filter "*.whl" |
| 196 | + $allValid = $true |
| 197 | + |
| 198 | + foreach ($wheel in $wheels) { |
| 199 | + # Check if wheel is a valid ZIP file |
| 200 | + try { |
| 201 | + Add-Type -AssemblyName System.IO.Compression.FileSystem |
| 202 | + $zip = [System.IO.Compression.ZipFile]::OpenRead($wheel.FullName) |
| 203 | + $entryCount = $zip.Entries.Count |
| 204 | + $zip.Dispose() |
| 205 | + |
| 206 | + Write-Host "✓ $($wheel.Name) - Valid ($entryCount entries)" |
| 207 | + } |
| 208 | + catch { |
| 209 | + Write-Error "✗ $($wheel.Name) - INVALID: $_" |
| 210 | + $allValid = $false |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + if (-not $allValid) { |
| 215 | + Write-Error "One or more wheel files are corrupted" |
| 216 | + exit 1 |
| 217 | + } |
| 218 | + |
| 219 | + Write-Host "`nAll wheels verified successfully!" |
| 220 | + |
| 221 | + # Step 5: Publish Symbols (if enabled and symbols exist) |
| 222 | + - ${{ if eq(parameters.publishSymbols, true) }}: |
| 223 | + - template: /OneBranchPipelines/steps/symbol-publishing-step.yml@self |
| 224 | + parameters: |
| 225 | + SymbolsFolder: '$(Build.SourcesDirectory)/symbols' |
| 226 | + |
| 227 | + # Step 6: Copy wheels to ob_outputDirectory for OneBranch artifact publishing |
| 228 | + - task: CopyFiles@2 |
| 229 | + displayName: '[TEST] Stage Wheels for Dummy Release' |
| 230 | + inputs: |
| 231 | + SourceFolder: '$(Build.SourcesDirectory)/dist' |
| 232 | + Contents: '*.whl' |
| 233 | + TargetFolder: '$(ob_outputDirectory)/release' |
| 234 | + flattenFolders: true |
| 235 | + |
| 236 | + # Step 7: ESRP Dummy Release Task (only if performDummyRelease is true) |
| 237 | + # ⚠️ IMPORTANT: Uses Maven ContentType for testing - NOT PyPI! |
| 238 | + - ${{ if eq(parameters.performDummyRelease, true) }}: |
| 239 | + - task: EsrpRelease@9 |
| 240 | + displayName: '[TEST] ESRP Dummy Release (Maven - NOT PyPI)' |
| 241 | + inputs: |
| 242 | + connectedservicename: '$(ESRPConnectedServiceName)' |
| 243 | + usemanagedidentity: true |
| 244 | + keyvaultname: '$(AuthAKVName)' |
| 245 | + signcertname: '$(AuthSignCertName)' |
| 246 | + clientid: '$(EsrpClientId)' |
| 247 | + Intent: 'PackageDistribution' |
| 248 | + # ⚠️ CRITICAL: ContentType is Maven (NOT PyPI) for safe testing |
| 249 | + # This ensures no accidental production releases to PyPI |
| 250 | + ContentType: 'Maven' |
| 251 | + ContentSource: 'Folder' |
| 252 | + FolderLocation: '$(Build.SourcesDirectory)/dist' |
| 253 | + WaitForReleaseCompletion: true |
| 254 | + Owners: '$(owner)' |
| 255 | + Approvers: '$(approver)' |
| 256 | + ServiceEndpointUrl: 'https://api.esrp.microsoft.com' |
| 257 | + MainPublisher: 'ESRPRELPACMAN' |
| 258 | + DomainTenantId: '$(DomainTenantId)' |
| 259 | + |
| 260 | + # Step 8: Show test release status |
| 261 | + - ${{ if eq(parameters.performDummyRelease, true) }}: |
| 262 | + - task: PowerShell@2 |
| 263 | + displayName: '[TEST] Dummy Release Summary' |
| 264 | + inputs: |
| 265 | + targetType: 'inline' |
| 266 | + script: | |
| 267 | + Write-Host "=====================================" |
| 268 | + Write-Host "⚠️ TEST PIPELINE - DUMMY RELEASE COMPLETED ⚠️" |
| 269 | + Write-Host "=====================================" |
| 270 | + Write-Host "Package: mssql-python (TEST)" |
| 271 | + Write-Host "Version: ${{ parameters.packageVersion }}" |
| 272 | + Write-Host "ContentType: Maven (NOT PyPI - Safe for Testing)" |
| 273 | + Write-Host "Owners: $(owner)" |
| 274 | + Write-Host "Approvers: $(approver)" |
| 275 | + Write-Host "Symbols Published: ${{ parameters.publishSymbols }}" |
| 276 | + Write-Host "=====================================" |
| 277 | + Write-Host "" |
| 278 | + Write-Host "⚠️ IMPORTANT: This was a DUMMY release using Maven ContentType" |
| 279 | + Write-Host " NO packages were released to PyPI" |
| 280 | + Write-Host "" |
| 281 | + Write-Host "What was tested:" |
| 282 | + Write-Host "✓ Artifact download from build pipeline" |
| 283 | + Write-Host "✓ Wheel integrity verification" |
| 284 | + if ("${{ parameters.publishSymbols }}" -eq "True") { |
| 285 | + Write-Host "✓ Symbol publishing to SqlClientDrivers org" |
| 286 | + } |
| 287 | + Write-Host "✓ ESRP release workflow (Maven ContentType)" |
| 288 | + Write-Host "" |
| 289 | + Write-Host "Next steps:" |
| 290 | + Write-Host "1. Verify dummy release in ESRP portal" |
| 291 | + Write-Host "2. Check ESRP approval workflow completion" |
| 292 | + Write-Host "3. Verify symbols in SqlClientDrivers org (if published)" |
| 293 | + Write-Host "4. For PRODUCTION release, use official-release-pipeline.yml" |
| 294 | + Write-Host "=====================================" |
| 295 | + |
| 296 | + - ${{ if eq(parameters.performDummyRelease, false) }}: |
| 297 | + - task: PowerShell@2 |
| 298 | + displayName: '[TEST] Dry Run - Dummy Release Skipped' |
| 299 | + inputs: |
| 300 | + targetType: 'inline' |
| 301 | + script: | |
| 302 | + Write-Host "=====================================" |
| 303 | + Write-Host "⚠️ TEST PIPELINE - DRY RUN MODE ⚠️" |
| 304 | + Write-Host "=====================================" |
| 305 | + Write-Host "Package: mssql-python (TEST)" |
| 306 | + Write-Host "Version: ${{ parameters.packageVersion }}" |
| 307 | + Write-Host "" |
| 308 | + Write-Host "Actions performed:" |
| 309 | + Write-Host "✓ Downloaded wheels from build pipeline" |
| 310 | + Write-Host "✓ Verified wheel integrity" |
| 311 | + Write-Host "✓ Downloaded symbols from build pipeline" |
| 312 | + if ("${{ parameters.publishSymbols }}" -eq "True") { |
| 313 | + Write-Host "✓ Published symbols to SqlClientDrivers org" |
| 314 | + } |
| 315 | + Write-Host "✗ ESRP dummy release NOT performed (parameter disabled)" |
| 316 | + Write-Host "" |
| 317 | + Write-Host "To test ESRP workflow:" |
| 318 | + Write-Host "1. Set 'performDummyRelease' parameter to true" |
| 319 | + Write-Host "2. Re-run this TEST pipeline" |
| 320 | + Write-Host "" |
| 321 | + Write-Host "For PRODUCTION release:" |
| 322 | + Write-Host "1. Use official-release-pipeline.yml instead" |
| 323 | + Write-Host "2. Official pipeline uses PyPI ContentType" |
| 324 | + Write-Host "=====================================" |
0 commit comments