diff --git a/.github/instructions/onebranch-pipeline-design.instructions.md b/.github/instructions/onebranch-pipeline-design.instructions.md index 1c848e2493..0860d5db52 100644 --- a/.github/instructions/onebranch-pipeline-design.instructions.md +++ b/.github/instructions/onebranch-pipeline-design.instructions.md @@ -1,534 +1,157 @@ --- applyTo: "eng/pipelines/**/*.yml" --- -# Multi-Product Azure DevOps Pipeline in dotnet/sqlclient — Design Specification +# OneBranch Pipeline Guidelines -## 1. Overview +## Purpose -This document describes the design of the unified Azure DevOps YAML pipeline that builds, signs, packages, and optionally releases six NuGet packages with interdependencies. The pipeline uses **stages** and **jobs** to maximize parallelism while respecting dependency order. It comprises five stages: three build stages, a validation stage, and an on-demand release stage. +Rules and conventions for editing the OneBranch Azure DevOps YAML pipelines that build, sign, package, and release six NuGet packages with interdependencies. -Two pipeline variants exist from the same stage/job structure: +## Pipeline Variants + +- `sqlclient-official.yml` — Official pipeline; uses `OneBranch.Official.CrossPlat.yml`; CI trigger on `internal/main` + daily schedule at 04:30 UTC +- `sqlclient-non-official.yml` — Non-Official pipeline; uses `OneBranch.NonOfficial.CrossPlat.yml`; manual only (`pr: none`, `trigger: none`) +- Both live under `eng/pipelines/onebranch/` and extend OneBranch governed templates +- Never parameterize the OneBranch template name — hardcode it per pipeline for PRC compliance +- Official pipeline must never be run on PRs or dev branches. + +## Package Dependency Order + +Respect this graph when modifying build stages: -| Pipeline | Template | Trigger | Purpose | -|----------|----------|---------|---------| -| `dotnet-sqlclient-official-pipeline.yml` | `OneBranch.Official.CrossPlat.yml` | CI + scheduled | Production-signed builds | -| `dotnet-sqlclient-non-official-pipeline.yml` | `OneBranch.NonOfficial.CrossPlat.yml` | Manual only | Validation / test builds (release in dry-run mode) | +1. `Microsoft.SqlServer.Server` — no dependencies +2. `Microsoft.Data.SqlClient.Internal.Logging` — no dependencies +3. `Microsoft.Data.SqlClient.Extensions.Abstractions` — depends on Logging +4. `Microsoft.Data.SqlClient` — depends on Logging + Abstractions +5. `Microsoft.Data.SqlClient.Extensions.Azure` — depends on Abstractions + Logging +6. `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` — depends on SqlClient + Abstractions + Logging + +## Build Stages -Both pipelines use the **OneBranch (1ES) governed template** infrastructure and share identical stage definitions, job templates, and variable chains. - ---- - -## 2. Products and Dependencies - -| # | Package | Dependencies | -|---|---------|-------------| -| 1 | `Microsoft.SqlServer.Server` | — | -| 2 | `Microsoft.Data.SqlClient.Internal.Logging` | — | -| 3 | `Microsoft.Data.SqlClient.Extensions.Abstractions` | `Internal.Logging` | -| 4 | `Microsoft.Data.SqlClient` | `Internal.Logging`, `Extensions.Abstractions` | -| 5 | `Microsoft.Data.SqlClient.Extensions.Azure` | `Extensions.Abstractions`, `Internal.Logging` | -| 6 | `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `SqlClient`, `Internal.Logging` | - ---- - -## 3. Pipeline Flow — Sequence Diagram - -```mermaid -sequenceDiagram - participant T as Trigger / User - participant P as Pipeline Orchestrator - participant B1a as Job: Build Internal.Logging - participant B1c as Job: Build SqlServer.Server - participant B1b as Job: Build Extensions.Abstractions - participant B2a as Job: Build SqlClient - participant B2b as Job: Build Extensions.Azure - participant V as Job: Validate MDS Package - participant B3 as Job: Build AKV Provider - participant R as Stage: Release - - Note over T,R: ══════ BUILD & SIGN PHASE ══════ - - T->>P: Pipeline triggered (CI / Scheduled / Manual) - - Note over P: Stage 1 — build_independent (parallel, no deps) - - par Stage 1 jobs (parallel) - P->>B1a: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Logging) - B1a-->>P: ✅ Signed .nupkg - and - P->>B1c: Build + ESRP sign + pack SqlServer.Server - B1c-->>P: ✅ Signed .nupkg - end - - Note over P: Stage 2 — build_abstractions (dependsOn: build_independent) - - P->>B1b: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Abstractions) - Note right of B1b: Downloads: Internal.Logging artifact - B1b-->>P: ✅ Signed .nupkg - - Note over P: Stage 3 — build_dependent (dependsOn: build_abstractions) - - par Stage 3 jobs (parallel) - P->>B2a: Build + ESRP sign + pack SqlClient - Note right of B2a: Downloads: Internal.Logging,
Extensions.Abstractions artifacts - B2a-->>P: ✅ Signed .nupkg + .snupkg - and - P->>B2b: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Azure) - Note right of B2b: Downloads: Extensions.Abstractions,
Internal.Logging artifacts - B2b-->>P: ✅ Signed .nupkg - end - - Note over P: Validation + Stage 4 (both dependsOn: build_dependent, run in parallel) - - par Validation and Stage 4 (parallel) - P->>V: Validate signed MDS package - V-->>P: ✅ Package validation passed - and - P->>B3: Build + ESRP sign + pack AKV Provider - Note right of B3: Downloads: SqlClient,
Internal.Logging artifacts - B3-->>P: ✅ Signed .nupkg - end - - Note over T,R: ══════ RELEASE PHASE (on-demand) ══════ - - alt At least one release parameter is true - P->>R: Stage: release (dependsOn: conditional on build stages) - Note right of R: ADO Environment Approval
(NuGet-Production environment) - R-->>P: ✅ Approved - Note right of R: Publish selected packages
via NuGetCommand@2 - R-->>P: ✅ Published to NuGet - else No release parameters set - Note over P: Release stage skipped - end - - Note over T,R: Pipeline complete 🎉 -``` - ---- - -## 4. Stage Design - -### 4.1 Build Phase - -The build phase runs automatically on every CI trigger, scheduled run, or manual queue. It is divided into four build stages plus a validation stage, based on the dependency graph. - -#### Stage 1 — `build_independent`: Independent Packages (no dependencies) - -| Job Template | Package | Build Target | Condition | -|--------------|---------|--------------|-----------| -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Internal.Logging` | `BuildLogging` / `PackLogging` | `buildAKVProvider OR buildSqlClient` | -| `build-signed-csproj-package-job.yml` | `Microsoft.SqlServer.Server` | `PackSqlServer` | `buildSqlServerServer` | - -- **`dependsOn`**: none -- **Parallelism**: Jobs run in parallel (depending on which are enabled) -- **Conditional builds**: Each job is wrapped with compile-time `${{ if }}` conditionals based on build parameters -- csproj-based jobs (`build-signed-csproj-package-job.yml`) perform: **Build DLLs → ESRP DLL signing → NuGet pack (NoBuild=true) → ESRP NuGet signing** → publish artifact - -#### Stage 2 — `build_abstractions`: Abstractions Package (depends on Stage 1) - -| Job Template | Package | Build Target | Artifact Dependencies | -|--------------|---------|--------------|----------------------| -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Extensions.Abstractions` | `BuildAbstractions` / `PackAbstractions` | `Internal.Logging` | - -- **Stage condition**: `buildSqlClient = true` (entire stage is excluded when false) -- **`dependsOn`**: `build_independent` -- Downloads `Microsoft.Data.SqlClient.Internal.Logging.nupkg` (from Stage 1) pipeline artifact - -#### Stage 3 — `build_dependent`: Core Packages (depend on Stage 2) - -| Job Template | Package | Build Target | Artifact Dependencies | -|--------------|---------|--------------|----------------------| -| `build-signed-package-job.yml` | `Microsoft.Data.SqlClient` | *(nuspec-based)* | `Internal.Logging`, `Extensions.Abstractions` | -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Extensions.Azure` | `BuildAzure` / `PackAzure` | `Extensions.Abstractions`, `Internal.Logging` | - -- **Stage condition**: `buildSqlClient = true` (entire stage is excluded when false) -- **`dependsOn`**: `build_abstractions` -- **Parallelism**: Both jobs run in parallel -- The MDS (SqlClient) job also publishes symbol packages (`.snupkg`) when `publishSymbols` is true -- All jobs configure APIScan with job-level `ob_sdl_apiscan_*` variables targeting package-specific folders - -#### Stage 4 — `build_addons`: Add-on Packages (depend on Stage 3) - -| Job Template | Package | Artifact Dependencies | -|--------------|---------|----------------------| -| `build-akv-official-job.yml` | `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `SqlClient`, `Internal.Logging` | - -- **Stage condition**: `buildAKVProvider AND buildSqlClient` (both must be true) -- **`dependsOn`**: `build_dependent` -- Downloads `Microsoft.Data.SqlClient.nupkg` (from Stage 3) and `Microsoft.Data.SqlClient.Internal.Logging.nupkg` (from Stage 1) pipeline artifacts -- Uses separate ESRP signing credentials (`Signing`-prefixed variables from `esrp-variables-v2` group) - -### 4.2 Validation Stage — `mds_package_validation` - -Validates the signed MDS (SqlClient) package after Stage 3 completes. - -- **Stage condition**: `buildSqlClient = true` -- **`dependsOn`**: `build_dependent` -- Runs in parallel with Stage 4 (`build_addons`) -- Uses `validate-signed-package-job.yml` template -- Downloads the `drop_build_dependent_build_signed_package` artifact and validates against `CurrentNetFxVersion` (default: `net462`) - -### 4.3 Release Phase — `release` - -The release stage is gated and only executes on demand when at least one release parameter is set to `true` at queue time. - -- **`dependsOn`**: Conditional based on which build stages are enabled: - - `build_independent` (when releasing SqlServer.Server or Logging) - - `build_abstractions` (when releasing Abstractions) - - `build_dependent`, `mds_package_validation` (when `buildSqlClient = true`) - - `build_addons` (when `buildAKVProvider AND buildSqlClient`) -- **Gate**: ADO Environment approvals (official pipeline only): - - Official: `NuGet-Production` environment with configured approvals - - Non-Official: `NuGet-DryRun` environment (no approvals, validation only) -- **Package selection**: Controlled by 6 runtime boolean parameters (see Section 5.2) -- **Stage condition**: The entire stage is skipped unless at least one release parameter is `true`: - ```yaml - - ${{ if or(parameters.releaseSqlServerServer, parameters.releaseLogging, ...) }}: - - stage: release - ``` -- **Publish jobs**: Each package has a conditional publish job that is included at compile time only when its parameter is `true`: - ```yaml - - ${{ if eq(parameters.releaseXxx, true) }}: - - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self - ``` -- **Environment variables**: Stage sets `ob_release_usedeploymentjob: true` for OneBranch integration: - - Official: `ob_release_environment: 'NuGet-Production'` - - Non-Official: `ob_release_environment: 'NuGet-DryRun'` - -#### Artifact → Publish Job Mapping - -| Package | Artifact Name | Publish Job | -|---------|---------------|-------------| -| `Microsoft.SqlServer.Server` | `drop_build_independent_build_package_SqlServer` | `publish_SqlServer_Server` | -| `Microsoft.Data.SqlClient.Internal.Logging` | `drop_build_independent_build_package_Logging` | `publish_Logging` | -| `Microsoft.Data.SqlClient.Extensions.Abstractions` | `drop_build_abstractions_build_package_Abstractions` | `publish_Abstractions` | -| `Microsoft.Data.SqlClient` | `drop_build_dependent_build_package_SqlClient` | `publish_SqlClient` | -| `Microsoft.Data.SqlClient.Extensions.Azure` | `drop_build_dependent_build_package_Azure` | `publish_Extensions_Azure` | -| `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `drop_build_addons_buildSignedAkvPackage` | `publish_AKVProvider` | - -Each publish job uses the reusable `publish-nuget-package-job.yml` template, which downloads the artifact and pushes `.nupkg`/`.snupkg` files via `NuGetCommand@2` with an external feed service connection. - -#### Dry-Run Mode - -Two ADO environments control release behavior: - -| Environment | Pipeline | Behavior | -|------------|----------|----------| -| `NuGet-DryRun` | Non-Official | Validation only — packages are never pushed | -| `NuGet-Production` | Official | Real releases with approval gate | - -**Non-official pipeline**: Always runs in dry-run mode. There is no `releaseDryRun` parameter — `dryRun: true` is hardcoded in every publish job. This prevents accidental publication from validation builds. - -**Official pipeline**: Exposes a `releaseDryRun` parameter (default: `true` for safety). When enabled, the template downloads artifacts and lists the `.nupkg`/`.snupkg` files that *would* be published but skips the actual `NuGetCommand@2` push. Set `releaseDryRun: false` to perform real pushes after final validation. - ---- - -## 5. Runtime Parameters - -### 5.1 Build Parameters - -The pipeline exposes the following parameters at queue time: - -```yaml -parameters: - - name: debug - displayName: 'Enable debug output' - type: boolean - default: false - - - name: publishSymbols - displayName: 'Publish symbols' - type: boolean - default: false - - - name: CurrentNetFxVersion - displayName: 'Lowest supported .NET Framework version (MDS validation)' - type: string - default: 'net462' - - - name: isPreview - displayName: 'Is this a preview build?' - type: boolean - default: false - - - name: testJobTimeout - displayName: 'Test job timeout (in minutes)' - type: number - default: 60 - - # Build parameters — control which packages to build - - name: buildSqlServerServer - displayName: 'Build Microsoft.SqlServer.Server' - type: boolean - default: true - - - name: buildSqlClient - displayName: 'Build Microsoft.Data.SqlClient and Extensions' - type: boolean - default: true - - - name: buildAKVProvider - displayName: 'Build Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider' - type: boolean - default: true -``` - -The `isPreview` parameter controls version resolution — when `true`, each package uses its preview version (e.g., `loggingPackagePreviewVersion`) instead of the GA version (e.g., `loggingPackageVersion`). All versions are defined in the centralized `libraries/common-variables.yml`. - -The build parameters enable selective package building: -- `buildSqlServerServer` — controls SqlServer.Server build job -- `buildSqlClient` — controls MDS, Extensions.Azure, Abstractions, Logging (when AKV is disabled), and validation stages -- `buildAKVProvider` — controls AKV Provider build (also requires `buildSqlClient=true`) and Logging (when SqlClient is disabled) - -When set to `false`, the respective jobs/stages are excluded at compile-time using `${{ if }}` conditionals. This allows faster pipeline runs when only certain packages need to be built. - -### 5.2 Release Parameters - -Six boolean parameters control selective package release. All default to `false` so the release stage is skipped on normal CI/scheduled builds: - -```yaml -parameters: - - name: releaseSqlServerServer - displayName: 'Release Microsoft.SqlServer.Server' - type: boolean - default: false - - - name: releaseLogging - displayName: 'Release Microsoft.Data.SqlClient.Internal.Logging' - type: boolean - default: false - - - name: releaseAbstractions - displayName: 'Release Microsoft.Data.SqlClient.Extensions.Abstractions' - type: boolean - default: false - - - name: releaseSqlClient - displayName: 'Release Microsoft.Data.SqlClient' - type: boolean - default: false - - - name: releaseAzure - displayName: 'Release Microsoft.Data.SqlClient.Extensions.Azure' - type: boolean - default: false - - - name: releaseAKVProvider - displayName: 'Release Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider' - type: boolean - default: false -``` - -#### Dry-Run Parameter (Official Pipeline Only) - -The **official pipeline** includes a `releaseDryRun` parameter that defaults to `true` for safety: - -```yaml - - name: releaseDryRun - displayName: 'Release Dry Run (do not push to NuGet)' - type: boolean - default: true # safety default — must explicitly disable for real releases -``` - -When `releaseDryRun: true`, publish jobs download artifacts and list packages but skip actual NuGet push. Set to `false` for production releases. - -> **Note**: The non-official pipeline does **not** expose this parameter — dry-run mode is hardcoded and cannot be disabled. - ---- - -## 6. Variable & Version Management - -### 6.1 Variable Chain - -Variables are defined in a layered template chain. All variable groups live inside the templates — none are declared inline at the pipeline level: - -``` -dotnet-sqlclient-official-pipeline.yml - └─ libraries/variables.yml - └─ libraries/build-variables.yml - ├─ group: 'Release Variables' - ├─ group: 'Symbols publishing' ← SymbolsPublishServer, SymbolsPublishTokenUri, etc. - └─ libraries/common-variables.yml - ├─ group: 'ESRP Federated Creds (AME)' ← ESRP signing credentials - ├─ SymbolServer / SymbolTokenUri aliases ← mapped from Symbols publishing group - └─ all package versions, paths, build variables -``` - -### 6.2 Package Version Variables - -All package versions are centralized in `libraries/common-variables.yml`: - -| Package | GA Version Var | Preview Version Var | Assembly Version Var | -|---------|---------------|--------------------|--------------------| -| Logging | `loggingPackageVersion` | `loggingPackagePreviewVersion` | `loggingAssemblyFileVersion` | -| Abstractions | `abstractionsPackageVersion` | `abstractionsPackagePreviewVersion` | `abstractionsAssemblyFileVersion` | -| SqlServer.Server | `sqlServerPackageVersion` | `sqlServerPackagePreviewVersion` | `sqlServerAssemblyFileVersion` | -| SqlClient (MDS) | `mdsPackageVersion` | `mdsPackagePreviewVersion` | `mdsAssemblyFileVersion` | -| Extensions.Azure | `azurePackageVersion` | `azurePackagePreviewVersion` | `azureAssemblyFileVersion` | -| AKV Provider | `akvPackageVersion` | `akvPackagePreviewVersion` | `akvAssemblyFileVersion` | - -The pipeline resolves `effective*Version` variables at compile time based on the `isPreview` parameter. - -### 6.3 Release & Symbol Variables - -| Variable | Defined In | Purpose | -|----------|-----------|---------| -| `NuGetServiceConnection` | `libraries/common-variables.yml` | External NuGet service connection name for `NuGetCommand@2` push | -| `SymbolServer` | `libraries/common-variables.yml` (alias) | Alias for `$(SymbolsPublishServer)` — used by MDS `publish-symbols-step.yml` | -| `SymbolTokenUri` | `libraries/common-variables.yml` (alias) | Alias for `$(SymbolsPublishTokenUri)` — used by MDS `publish-symbols-step.yml` | - -### 6.4 Variable Groups - -| Group | Included In | Purpose | -|-------|------------|---------| -| `Release Variables` | `build-variables.yml` | Release-specific configuration | -| `Symbols publishing` | `build-variables.yml` | Symbol publishing credentials (`SymbolsAzureSubscription`, `SymbolsPublishServer`, `SymbolsPublishTokenUri`, `SymbolsUploadAccount`, `SymbolsPublishProjectName`) | -| `ESRP Federated Creds (AME)` | `common-variables.yml` | Federated identity for ESRP signing (`ESRPConnectedServiceName`, `ESRPClientId`, `AppRegistrationClientId`, `AppRegistrationTenantId`, `AuthAKVName`, `AuthSignCertName`) | - ---- - -## 7. Code Signing (ESRP) - -All packages are signed using **ESRP (Enterprise Security Release Pipeline)** with federated identity authentication. - -### Signing Flow (per job) - -#### csproj-based Extension Packages (Logging, Abstractions, Azure) -1. **Build DLLs only** — `build.proj` target (e.g., `BuildLogging`) compiles assemblies without creating NuGet packages -2. **ESRP DLL signing** — Assemblies are signed with Authenticode certificates via ESRP -3. **NuGet pack** — `build.proj` pack target (e.g., `PackLogging`) creates `.nupkg` from signed DLLs using `NoBuild=true` -4. **ESRP NuGet signing** — The `.nupkg` files are signed with NuGet certificates via ESRP - -This workflow ensures the NuGet package contains **signed DLLs** rather than signing the NuGet package around unsigned assemblies. - -#### nuspec-based Packages (SqlServer.Server, SqlClient, AKV Provider) -1. **Build + pack** — MSBuild creates both assemblies and NuGet packages -2. **ESRP DLL signing** — Assemblies are signed with Authenticode certificates via ESRP -3. **ESRP NuGet signing** — The `.nupkg` files are signed with NuGet certificates via ESRP - -### Credential Model - -- Extension packages (Logging, Abstractions, Azure, SqlServer.Server, SqlClient) use the primary ESRP credentials from the `ESRP Federated Creds (AME)` variable group (loaded via `common-variables.yml`) -- AKV Provider uses separate `Signing`-prefixed credential parameters that are passed explicitly to the `build-akv-official-job.yml` template -- All credentials are sourced from Azure Key Vault and federated identity — no secrets stored in pipeline YAML - ---- - -## 8. SDL & Compliance (OneBranch) - -Both pipelines use **OneBranch governed templates** for 1ES compliance. The SDL configuration differs between Official and Non-Official: - -| SDL Tool | Official | Non-Official | Purpose | -|----------|----------|--------------|---------| -| **TSA** | ✅ `enabled: true` | ❌ `enabled: false` | Uploads SDL results to TSA for downstream analysis | -| **ApiScan** | ✅ `enabled: true`, `break: true` | ✅ `enabled: true`, `break: true` | Scans APIs for compliance issues | -| **CodeQL** | ✅ (non-preview) | ✅ (non-preview) | Static analysis for security vulnerabilities | -| **SBOM** | ✅ (non-preview) | ✅ (non-preview) | Software Bill of Materials generation | -| **Policheck** | ✅ `break: true` | ✅ `break: true` | Scans for policy-violating content | -| **BinSkim** | ✅ (async, non-preview) | ✅ (async, non-preview) | Binary security analysis | -| **CredScan** | ✅ (async, non-preview) | ✅ (async, non-preview) | Credential leak detection | -| **Roslyn** | ✅ (async, non-preview) | ✅ (async, non-preview) | Roslyn-based security analyzers | -| **Armory** | ✅ `break: true` | ✅ `break: true` | Additional security scanning | - -### APIScan Configuration - -APIScan is configured at **both pipeline level and job level**: - -**Pipeline-level** (`globalSdl:apiscan:`): Sets default configuration inherited by all jobs. This is configured for MDS (Microsoft.Data.SqlClient) as the primary product. - -**Job-level** (`ob_sdl_apiscan_*` variables): Each build job overrides the pipeline defaults with package-specific settings: - -| Variable | Purpose | -|----------|---------| -| `ob_sdl_apiscan_enabled` | Enable/disable APIScan for this job (`true`) | -| `ob_sdl_apiscan_softwareFolder` | Path to signed DLLs for scanning | -| `ob_sdl_apiscan_symbolsFolder` | Path to PDBs for scanning | -| `ob_sdl_apiscan_softwarename` | Package name (e.g., `Microsoft.Data.SqlClient.Internal.Logging`) | -| `ob_sdl_apiscan_versionNumber` | Assembly file version | - -Each job copies its signed DLLs and PDBs to a package-specific folder under `$(Build.SourcesDirectory)/apiScan//` after ESRP DLL signing, ensuring APIScan analyzes the correct signed binaries for each package. - -> **PRC Compliance**: The Official pipeline hardcodes `OneBranch.Official.CrossPlat.yml` (not parameterized) to satisfy Production Readiness Check static verification requirements. - ---- - -## 9. Artifact Strategy - -- Each build job publishes its output as a **pipeline artifact** managed by OneBranch's `ob_outputDirectory` convention. -- Artifact names follow the OneBranch auto-generated pattern: `drop__` (e.g., `drop_build_dependent_build_package_SqlClient`). -- Downstream stages use `DownloadPipelineArtifact@2` to pull required packages into a local directory. -- A local NuGet source is configured at build time pointing to the downloaded artifacts directory so `dotnet restore` resolves internal dependencies. - ---- - -## 10. Trigger Configuration - -### Official Pipeline (`dotnet-sqlclient-official-pipeline.yml`) - -```yaml -trigger: - branches: - include: - - internal/main - paths: - include: - - .azuredevops - - .config - - doc - - eng/pipelines - - src - - tools - - azurepipelines-coverage.yml - - build.proj - - NuGet.config - -schedules: - - cron: '30 4 * * Mon' # Weekly Sunday 9:30 PM (UTC-7) - branches: { include: [internal/main] } - always: true - - cron: '30 3 * * Mon-Fri' # Weekday 8:30 PM (UTC-7) - branches: { include: [internal/main] } -``` - -- **CI trigger**: Runs on pushes to `internal/main` when relevant paths change -- **Scheduled**: Weekly full build (Sundays) + weekday builds (Mon–Fri) -- **No PR trigger**: Official pipeline should not run on PRs (separate PR pipelines exist) - -### Non-Official Pipeline (`dotnet-sqlclient-non-official-pipeline.yml`) - -```yaml -trigger: none -pr: none -``` - -- **Manual only**: Queued on-demand for validation/test builds - ---- - -## 11. Infrastructure - -| Concern | Implementation | -|---------|---------------| -| **Pipeline template** | OneBranch governed templates (`OneBranch.Pipelines/GovernedTemplates`) | -| **Build agents** | OneBranch-managed Windows containers (`WindowsHostVersion: 1ESWindows2022`) | -| **.NET SDK** | Pinned via `global.json` (with `useGlobalJson: true` in install steps) | -| **Code signing** | ESRP v2 with federated identity (Azure Key Vault backed) | -| **Symbol publishing** | Optional, controlled by `publishSymbols` parameter; uses `Symbols publishing` variable group (aliases `SymbolServer`/`SymbolTokenUri` defined in `common-variables.yml`) | - ---- - -## 12. Key Design Decisions - -1. **Single pipeline, multiple stages** — avoids managing 6 separate pipelines while keeping clear separation of concerns. -2. **Official + Non-Official variants** — hardcoded OneBranch templates (no parameterized `oneBranchType`) for PRC compliance; Non-Official variant allows manual validation builds. -3. **Parallel jobs within stages** — minimizes total wall-clock time; only waits where dependencies demand it. -4. **Pipeline artifacts over Universal Packages** — faster, ephemeral, scoped to the run; appropriate for build-time dependency resolution. -5. **ESRP-based code signing** — all DLLs and NuGet packages are signed in-pipeline using ESRP with federated identity; no secrets in YAML. -6. **Centralized version management** — all 6 package versions (GA + preview) defined once in `libraries/common-variables.yml`; `isPreview` toggle selects the active set. -7. **Dependency-aware stage ordering** — ensures packages are always built after their dependencies, guaranteeing consistent, reproducible builds. -8. **Validation in parallel with Stage 3** — MDS package validation runs alongside AKV Provider build (both depend on Stage 2), reducing total pipeline duration. -9. **Selective on-demand release** — 6 boolean parameters control which packages are published; the release stage is entirely skipped when none are selected, keeping normal CI builds unaffected. -10. **ADO Environment approval gate** — two environments: `NuGet-Production` (official, with configured approvals) and `NuGet-DryRun` (non-official, validation only). Both use `ob_release_environment` for OneBranch integration. -11. **Compile-time conditional publish jobs** — `${{ if eq(parameters.releaseXxx, true) }}` template expansion ensures unselected publish jobs are excluded entirely from the pipeline run (not just skipped at runtime). -12. **Mandatory dry-run for non-official** — the non-official variant hardcodes `dryRun: true` (no parameter), preventing accidental publication. The official variant defaults `releaseDryRun: true` for safety but allows override for actual releases. -13. **Selective build parameters** — `buildSqlClient`, `buildSqlServerServer`, and `buildAKVProvider` allow building subsets of packages, with dependency-aware conditionals ensuring Logging builds when either SqlClient or AKV is needed. +Defined in `stages/build-stages.yml`. Four build stages plus validation, ordered by dependency: + +- **`build_independent`** (Stage 1) — Logging and SqlServer.Server in parallel; no inter-package dependencies +- **`build_abstractions`** (Stage 2) — Abstractions; `dependsOn: build_independent`; downloads Logging artifact +- **`build_dependent`** (Stage 3) — SqlClient and Extensions.Azure in parallel; `dependsOn: build_abstractions`; downloads Abstractions + Logging artifacts +- **`build_addons`** (Stage 4) — AKV Provider; `dependsOn: build_dependent`; downloads SqlClient + Abstractions + Logging artifacts +- **`mds_package_validation`** — Validates signed SqlClient package; `dependsOn: build_dependent`; runs in parallel with Stage 4 + +Each build job copies PDB files into `$(JOB_OUTPUT)/symbols/` so they are included in the auto-published pipeline artifact alongside the NuGet packages in `$(JOB_OUTPUT)/packages/`. + +Stage conditional rules: +- Wrap stages/jobs in `${{ if }}` compile-time conditionals based on build parameters +- `buildSqlClient` controls Stages 2, 3, validation, and Logging (when AKV is disabled) +- `buildAKVProvider AND buildSqlClient` controls Stage 4 +- `buildSqlServerServer` controls SqlServer.Server job in Stage 1 +- Logging builds when `buildAKVProvider OR buildSqlClient` is true + +## Job Templates + +- **`build-signed-csproj-package-job.yml`** — Generic job for csproj-based packages (Logging, SqlServer.Server, Abstractions, Azure, AKV Provider). Flow: Build DLLs → ESRP DLL signing → NuGet pack (`NoBuild=true`) → ESRP NuGet signing → Copy PDBs to artifact +- **`build-signed-sqlclient-package-job.yml`** — SqlClient-specific job (nuspec-based). Flow: Build all configurations → ESRP DLL signing (main + resource DLLs) → NuGet pack via nuspec → ESRP NuGet signing → Copy PDBs to artifact +- **`validate-signed-package-job.yml`** — Validates signed MDS package (signature, strong names, folder structure, target frameworks) +- **`publish-nuget-package-job.yml`** — Reusable release job using OneBranch `templateContext.type: releaseJob` with `inputs` for artifact download; pushes via `NuGetCommand@2` +- **`publish-symbols-job.yml`** — Reusable symbols job: downloads a build artifact, locates PDBs under `symbols/`, and invokes `publish-symbols-step.yml` + +When adding a new csproj-based package: +- Use `build-signed-csproj-package-job.yml` with appropriate `packageName`, `packageFullName`, `versionProperties`, and `downloadArtifacts` +- Add build and pack targets to `build.proj` +- Add version variables to `variables/common-variables.yml` +- Add artifact name variable to `variables/onebranch-variables.yml` + +## Symbols Publishing Stage + +- Defined in `stages/publish-symbols-stage.yml`; produces stage `publish_symbols` +- Entire stage excluded at compile time when `publishSymbols` is false +- `dependsOn` is conditional based on which `build*` parameters are set, mirroring the build stage dependency graph +- One job per package (`publish-symbols-job.yml`), each downloading its build artifact and publishing PDBs from `symbols/` +- Each package's PDBs are published separately with unique artifact names and version information +- Build jobs copy PDBs into `$(JOB_OUTPUT)/symbols/` so they are included in the auto-published artifact +- The `publish-symbols-step.yml` accepts a `symbolsFolder` parameter to point at the downloaded PDB location +- The publish step calls an extracted `publish-symbols.ps1` script with structured error handling and diagnostic logging +- Symbols publishing credentials come from the `Symbols Publishing` variable group + +## Release Stage + +- Defined in `stages/release-stages.yml`; produces stage `release_production` (official) or `release_test` (non-official) via `stageNameSuffix` parameter +- Entire stage excluded at compile time when no release parameters are true +- `dependsOn` is conditional based on which release parameters are set +- `releaseToProduction` parameter controls NuGet target feed: + - `true` → service connection `ADO Nuget Org Connection` (NuGet Production) + - `false` → service connection `ADO Nuget Org Test Connection` (NuGet Test) +- Non-official pipeline always sets `releaseToProduction: false` +- Environment gating: + - Official: `ob_release_environment: Production`, `ob_deploymentjob_environment: NuGet-Production` + - Non-official: `ob_release_environment: Test`, `ob_deploymentjob_environment: NuGet-DryRun` +- Each publish job uses OneBranch deployment job syntax (`templateContext.type: releaseJob` with `inputs` for artifact download) + +## Parameters + +Build parameters (all boolean, default `true`): +- `debug` — enable debug output (default `false`) +- `isPreview` — use preview version numbers (default `false`) +- `publishSymbols` — publish symbols to servers (default `false`) +- `buildSqlServerServer` — build SqlServer.Server package +- `buildSqlClient` — build SqlClient, Extensions.Azure, Abstractions, and Logging +- `buildAKVProvider` — build AKV Provider (requires `buildSqlClient`) + +Release parameters (all boolean, default `false`): +- `releaseSqlServerServer`, `releaseLogging`, `releaseAbstractions`, `releaseSqlClient`, `releaseAzure`, `releaseAKVProvider` + +Official-only parameter: +- `releaseToProduction` — push to NuGet Production feed (default `false`) + +When `isPreview` is true, pipeline resolves `effective*Version` variables to preview versions; otherwise GA versions. All versions defined in `variables/common-variables.yml`. + +## Variables and Versions + +- Variable chain: pipeline YAML → `variables/onebranch-variables.yml` → `variables/common-variables.yml` +- All package versions (GA, preview, assembly file) centralized in `variables/common-variables.yml` +- `effective*Version` pipeline variables map to selected version set based on `isPreview` +- Artifact name variables defined in `variables/onebranch-variables.yml` following `drop__` pattern +- `assemblyBuildNumber` derived from first segment of `Build.BuildNumber` only (16-bit limit) +- When adding a new package, add GA version, preview version, and assembly file version entries + +Variable groups: +- `Symbols Publishing` — symbol publishing credentials (in `onebranch-variables.yml`) +- `ESRP Federated Creds (AME)` — ESRP signing credentials (in `common-variables.yml`) + +## Code Signing (ESRP) + +- Uses ESRP v6 tasks (`EsrpMalwareScanning@6`, `EsrpCodeSigning@6`) with MSI/federated identity authentication +- Signing only runs when `isOfficial: true` — non-official pipelines skip ESRP steps +- csproj-based packages: sign DLLs first → pack with `NoBuild=true` → sign NuGet package (ensures NuGet contains signed DLLs) +- SqlClient: sign DLLs (including resource DLLs) → nuspec pack → sign NuGet package +- DLL signing uses keyCode `CP-230012` (Authenticode); NuGet signing uses keyCode `CP-401405` +- All ESRP credentials come from variable groups — never hardcode secrets in YAML + +## SDL and Compliance + +- TSA: enabled only in official pipeline; disabled in non-official to avoid spurious alerts +- ApiScan: enabled in both; currently `break: false` pending package registration +- Each build job sets `ob_sdl_apiscan_softwareFolder` to `$(JOB_OUTPUT)/assemblies` and `ob_sdl_apiscan_symbolsFolder` to `$(JOB_OUTPUT)/symbols` +- CodeQL, SBOM, Policheck (`break: true`): enabled in both pipelines +- asyncSdl `enabled: false` in both; individual sub-tools (CredScan, BinSkim, Armory, Roslyn) configured underneath +- Policheck exclusions: `$(REPO_ROOT)\.config\PolicheckExclusions.xml` +- CredScan suppressions: `$(REPO_ROOT)/.config/CredScanSuppressions.json` + +## Artifact Conventions + +- `ob_outputDirectory` set to `$(JOB_OUTPUT)` (= `$(REPO_ROOT)/output`) — OneBranch auto-publishes this directory +- Each published artifact uses subdirectories to separate file types: + - `assemblies/` — DLL assemblies for APIScan (preserving TFM folder structure) + - `packages/` — NuGet packages (`.nupkg`, `.snupkg`) + - `symbols/` — PDB symbol files (preserving TFM folder structure, shared by APIScan and symbol publishing) +- Artifact names follow `drop__` — defined in `variables/onebranch-variables.yml` +- Downstream jobs download artifacts via `DownloadPipelineArtifact@2` into `$(Build.SourcesDirectory)/packages` +- Downloaded packages serve as a local NuGet source for `dotnet restore` +- If stage or job names change, update artifact name variables in `onebranch-variables.yml` + +## Common Pitfalls + +- Do not use `PublishPipelineArtifacts` task — OneBranch auto-publishes from `ob_outputDirectory` +- Do not add `NuGetToolInstaller@1` in OneBranch containers — NuGet is pre-installed +- Variable templates are under `variables/` not `libraries/` +- Always test parameter changes in the non-official pipeline first +- When modifying stage names, update all `dependsOn` references and artifact name variables +- Release jobs must use `templateContext.type: releaseJob` with `inputs` for artifact download — deployment jobs do not auto-download artifacts diff --git a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml index 01d9b61da1..94e6438ef5 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml @@ -15,6 +15,29 @@ # signed DLLs, and finally ESRP signs the NuGet packages. parameters: + + # Signing Parameters ----------------------------------------------------- + + - name: signingAppRegistrationClientId + type: string + + - name: signingAppRegistrationTenantId + type: string + + - name: signingAuthAkvName + type: string + + - name: signingAuthSignCertName + type: string + + - name: signingEsrpClientId + type: string + + - name: signingEsrpConnectedServiceName + type: string + + # OTHERS +===================================== + # Short package name used in the job name, display strings, filesystem paths, and as a suffix for # the default Build and Pack targets if those aren't specified. - name: packageName @@ -31,31 +54,23 @@ parameters: - name: packageFullName type: string - # The version of the package. This is used for symbol publishing. It is not used for the DLL or - # NuGet package versions since those are supplied via the versionProperties parameter. - - name: packageVersion - type: string - # The MSBuild build target in build.proj (e.g. BuildLogging). If not specified, defaults to # Build. - name: buildTarget type: string default: "" + # True to enable ESRP malware scanning and code signing steps, which should not be + # run on non-official pipelines as they access production resources. + - name: isOfficial + type: boolean + # The MSBuild pack target in build.proj (e.g. PackLogging). If not specified, defaults to # Pack. - name: packTarget type: string default: "" - # The C# build configuration to build (e.g. Debug or Release). - - name: buildConfiguration - type: string - values: - - Debug - - Release - default: Release - # Additional MSBuild -p: arguments for version properties. These may include versions of # packages this package depends on, or versions for this package itself. - name: versionProperties @@ -66,29 +81,6 @@ parameters: - name: assemblyFileVersion type: string - # True to publish symbols to private and public servers. - - name: publishSymbols - type: boolean - - # Values required by ESRP tasks. - - name: esrpConnectedServiceName - type: string - - - name: esrpClientId - type: string - - - name: appRegistrationClientId - type: string - - - name: appRegistrationTenantId - type: string - - - name: authAkvName - type: string - - - name: authSignCertName - type: string - # Optional list of pipeline artifacts to download before building. Each entry is an object # with 'artifactName' (the pipeline artifact name) and 'displayName' (used in the task label). # This replaces hard-coded packageName conditionals so callers declare their own dependencies. @@ -103,15 +95,20 @@ jobs: type: windows variables: - ob_outputDirectory: $(PACK_OUTPUT) + # Inform OneBranch that files put in this directory should be uploaded as artifacts. + ob_outputDirectory: $(JOB_OUTPUT) + # APIScan configuration for this Extension package ob_sdl_apiscan_enabled: true - ob_sdl_apiscan_softwareFolder: $(Build.SourcesDirectory)/apiScan/${{ parameters.packageName }}/dlls - ob_sdl_apiscan_symbolsFolder: $(Build.SourcesDirectory)/apiScan/${{ parameters.packageName }}/pdbs + ob_sdl_apiscan_softwareFolder: $(JOB_OUTPUT)/assemblies + ob_sdl_apiscan_symbolsFolder: $(JOB_OUTPUT)/symbols ob_sdl_apiscan_softwarename: ${{ parameters.packageFullName }} ob_sdl_apiscan_versionNumber: ${{ parameters.assemblyFileVersion }} + # If parameters.buildTarget is not provided, default to "Build{parameters.packageName}" buildTarget: ${{ coalesce(parameters.buildTarget, format('Build{0}', parameters.packageName)) }} + + # If parameters.packTarget is not provided, default to "Pack{parameters.packageName}" packTarget: ${{ coalesce(parameters.packTarget, format('Pack{0}', parameters.packageName)) }} steps: @@ -123,79 +120,97 @@ jobs: displayName: Download ${{ artifact.displayName }} inputs: artifactName: ${{ artifact.artifactName }} - targetPath: $(Build.SourcesDirectory)/packages + targetPath: $(JOB_INPUT) # Install the .NET SDK. - template: /eng/pipelines/steps/install-dotnet.yml@self # Perform Roslyn analysis before building, since this step will clobber build output. - - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self + - template: /eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml@self parameters: + buildTarget: '${{ variables.buildTarget }}' msBuildArguments: >- - -t:$(buildTarget) - -p:Configuration=${{ parameters.buildConfiguration }} + -p:Configuration=Release -p:ReferenceType=Package ${{ parameters.versionProperties }} # Build the package, producing DLLs only (no NuGet package yet). - - template: /eng/pipelines/onebranch/steps/compound-build-csproj-step.yml@self + - template: /eng/pipelines/onebranch/steps/build-csproj-step.yml@self parameters: - buildTarget: $(buildTarget) - buildConfiguration: ${{ parameters.buildConfiguration }} + buildTarget: '${{ variables.buildTarget }}' versionProperties: ${{ parameters.versionProperties }} - # ESRP sign the DLLs. - - template: /eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml@self - parameters: - appRegistrationClientId: ${{ parameters.appRegistrationClientId }} - appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - authAkvName: ${{ parameters.authAkvName }} - authSignCertName: ${{ parameters.authSignCertName }} - esrpClientId: ${{ parameters.esrpClientId }} - esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - pattern: ${{ parameters.packageFullName }}.dll - - # Copy signed DLLs and PDBs to APIScan folders. + - ${{ if eq(parameters.isOfficial, true) }}: + # ESRP sign the DLLs. + - template: /eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml@self + parameters: + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + # Minimatch pattern (multi-line, one pattern per line). + # **/ matches any nested directory (e.g. net8.0/, netstandard2.0/fr/). + # + # Matches: net8.0/.dll + # netstandard2.0/fr/.resources.dll + pattern: | + **/${{ parameters.packageFullName }}.dll + **/${{ parameters.packageFullName }}.resources.dll + + # Copy DLLs to the assemblies/ subdirectory for APIScan. - task: CopyFiles@2 displayName: Copy DLLs for APIScan inputs: SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: "**/${{ parameters.packageFullName }}.dll" - TargetFolder: $(ob_sdl_apiscan_softwareFolder) + # Matches: .dll (main assembly) across all TFM subdirs + # .resources.dll (localized satellite assemblies in locale subdirs, if any) + Contents: | + **/${{ parameters.packageFullName }}.dll + **/${{ parameters.packageFullName }}.resources.dll + TargetFolder: $(JOB_OUTPUT)/assemblies # We must preserve the folder structure since our C# projects may produce multiple # identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0, # etc.), and we need to keep those separate for APIScan to work correctly. flattenFolders: false + # Copy PDBs into the output directory so they are included in the published pipeline + # artifact. The symbols publishing stage will download this artifact and publish PDBs + # for this package using the files under symbols/. - task: CopyFiles@2 - displayName: Copy PDBs for APIScan + displayName: Copy PDBs for APIScan and Symbols Publishing inputs: SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: "**/${{ parameters.packageFullName }}.pdb" - TargetFolder: $(ob_sdl_apiscan_symbolsFolder) + # Matches: .pdb across all TFM subdirs. + # Note: Resource DLLs are resource-only satellite assemblies and do not produce PDBs. + Contents: '**/${{ parameters.packageFullName }}.pdb' + TargetFolder: $(JOB_OUTPUT)/symbols flattenFolders: false # Pack the signed DLLs into NuGet package (NoBuild=true). - - template: /eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml@self + - template: /eng/pipelines/onebranch/steps/pack-csproj-step.yml@self parameters: - packTarget: $(packTarget) - buildConfiguration: ${{ parameters.buildConfiguration }} + packTarget: ${{ variables.packTarget }} versionProperties: ${{ parameters.versionProperties }} - # ESRP sign the NuGet package. - - template: /eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml@self - parameters: - appRegistrationClientId: ${{ parameters.appRegistrationClientId }} - appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - authAkvName: ${{ parameters.authAkvName }} - authSignCertName: ${{ parameters.authSignCertName }} - esrpClientId: ${{ parameters.esrpClientId }} - esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - pattern: '${{ parameters.packageFullName }}.*nupkg' - - # Publish symbols to servers - - ${{ if eq(parameters.publishSymbols, true) }}: - - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self + - ${{ if eq(parameters.isOfficial, true) }}: + # ESRP sign the NuGet package. + - template: /eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml@self parameters: - packageFullName: ${{ parameters.packageFullName }} - packageVersion: ${{ parameters.packageVersion }} + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + searchPath: $(JOB_OUTPUT)/packages + # Minimatch pattern with extglob. + # [0-9] matches a single digit, anchoring to the version segment so + # similarly-prefixed package names are not matched. + # ?(s) is an extglob that optionally matches 's'. + # + # Matches: .1.0.0.nupkg + # .1.0.0-preview.1.snupkg + # Excludes: .SomeOther.1.0.0.nupkg + searchPattern: '${{ parameters.packageFullName }}.[0-9]*.?(s)nupkg' diff --git a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml index 98f5c811aa..83ac9325e6 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml @@ -4,135 +4,177 @@ # See the LICENSE file in the project root for more information. # ################################################################################# -# This file is only included in MDS OneBranch Official pipelines. +# Job that performs a build of Microsoft.Data.SqlClient using the build2.proj file. parameters: - # True to publish symbols to public and private feeds after the build completes. - - name: publishSymbols + # Whether this build is for an "official" OneBranch pipeline. This is used to enable ESRP signing + # on the artifacts of this job. + - name: isOfficial type: boolean - # True if this is a preview build, which uses the preview version numbers from - # common-variables.yml. - - name: isPreview - type: boolean + - name: signingAppRegistrationClientId + type: string + + - name: signingAppRegistrationTenantId + type: string + + - name: signingAuthAkvName + type: string + + - name: signingAuthSignCertName + type: string + + - name: signingEsrpClientId + type: string + + - name: signingEsrpConnectedServiceName + type: string + + # Package Parameters + - name: abstractionsArtifactName + type: string + + - name: abstractionsPackageVersion + type: string + + - name: loggingArtifactName + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientAssemblyFileVersion + type: string + + - name: sqlClientPackageVersion + type: string jobs: -- job: build_package_SqlClient - displayName: 'Build Microsoft.Data.SqlClient' - pool: - type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs - - variables: - ob_outputDirectory: $(PACK_OUTPUT) - # APIScan configuration for this Extension package - ob_sdl_apiscan_enabled: true - ob_sdl_apiscan_softwareFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/dlls - ob_sdl_apiscan_symbolsFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/pdbs - ob_sdl_apiscan_softwarename: Microsoft.Data.SqlClient - ob_sdl_apiscan_versionNumber: $(assemblyBuildNumber) - - ${{ if parameters.isPreview }}: - abstractionsPackageVersion: $(abstractionsPackagePreviewVersion) - loggingPackageVersion: $(loggingPackagePreviewVersion) - mdsPackageVersion: $(mdsPackagePreviewVersion) - - steps: - - script: SET - displayName: 'Print Environment Variables' - - # Download the Abstractions and Logging packages from the previous stage into - # packages/ so that they're available via the local NuGet feed when restoring MDS. - # MDS depends on both Extensions.Abstractions and Internal.Logging. - - task: DownloadPipelineArtifact@2 - displayName: Download Abstractions Package - inputs: - artifactName: $(abstractionsArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - - task: DownloadPipelineArtifact@2 - displayName: Download Logging Package - inputs: - artifactName: $(loggingArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - # Install the .NET SDK. - - template: /eng/pipelines/steps/install-dotnet.yml@self - - # Build our tooling, which is required by the analysis step below, but - # shouldn't be analyzed itself. - - task: MSBuild@1 - displayName: 'Build Tooling' - inputs: - solution: '**/build.proj' - configuration: Release - msbuildArguments: -t:BuildTools - - # Perform analysis before building, since this step will clobber build output. - - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self - - # Build MDS, producing signed DLLs. - - template: /eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml@self - parameters: - # These variables are sourced from common-variables.yml. - abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion) - abstractionsPackageVersion: $(abstractionsPackageVersion) - loggingAssemblyFileVersion: $(loggingAssemblyFileVersion) - loggingPackageVersion: $(loggingPackageVersion) - mdsAssemblyFileVersion: $(mdsAssemblyFileVersion) - mdsPackageVersion: $(mdsPackageVersion) - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: 'Microsoft.Data.SqlClient.dll' - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: 'Microsoft.Data.SqlClient.resources.dll' - - - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self - parameters: - buildConfiguration: Release - displayName: 'Create MDS NuGet Package' - generateSymbolsPackage: true - installNuget: false - nuspecPath: $(nuspecPath) - outputDirectory: $(PACK_OUTPUT) - packageVersion: $(mdsPackageVersion) - properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion);LoggingPackageVersion=$(loggingPackageVersion)' - referenceType: Package - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: pkg - nupkgPattern: 'Microsoft.Data.SqlClient.$(mdsPackageVersion).*nupkg' - - # Copy signed DLLs and PDBs to APIScan folders. - - task: CopyFiles@2 - displayName: Copy DLLs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: '**/Microsoft.Data.SqlClient.dll' - TargetFolder: $(ob_sdl_apiscan_softwareFolder) - # We must preserve the folder structure since our C# projects may produce multiple - # identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0, - # etc.), and we need to keep those separate for APIScan to work correctly. - flattenFolders: false - - - task: CopyFiles@2 - displayName: Copy PDBs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: '**/Microsoft.Data.SqlClient.pdb' - TargetFolder: $(ob_sdl_apiscan_symbolsFolder) - flattenFolders: false - - # Publish symbols to servers - - ${{ if eq(parameters.publishSymbols, true) }}: - - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self - parameters: - packageFullName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + - job: build_package_SqlClient + displayName: 'Build Microsoft.Data.SqlClient' + pool: + type: windows + + variables: + ob_outputDirectory: '$(JOB_OUTPUT)' + ob_sdl_apiscan_softwareFolder: $(JOB_OUTPUT)/assemblies + ob_sdl_apiscan_symbolsFolder: $(JOB_OUTPUT)/symbols + ob_sdl_apiscan_softwarename: 'Microsoft.Data.SqlClient' + ob_sdl_apiscan_versionNumber: ${{ parameters.sqlClientAssemblyFileVersion }} + + steps: + # Dump environment and parameters + - template: /eng/pipelines/onebranch/steps/script-output-environment-variables-step.yml@self + + - powershell: | + $jsonParams = '${{ convertToJson(parameters) }}' -replace '\\', '\\' + $jsonParams | ConvertFrom-Json | Format-List + displayName: 'Output Job Parameters' + + # Download Abstractions and Logging packages from previous stages into /packages/ so they + # are available via the local NuGet feed when restoring. + # @TODO: With the new build2.proj pack targets, this technically is not necessary. + - task: DownloadPipelineArtifact@2 + displayName: Download Microsoft.Data.SqlClient.Extensions.Abstractions Artifact + inputs: + artifactName: '${{ parameters.abstractionsArtifactName }}' + targetPath: '$(JOB_INPUT)' + + - task: DownloadPipelineArtifact@2 + displayName: Download Microsoft.Data.SqlClient.Extensions.Logging Artifact + inputs: + artifactName: '${{ parameters.loggingArtifactName }}' + targetPath: '$(JOB_INPUT)' + + # Install the .NET SDK + - template: /eng/pipelines/steps/install-dotnet.yml@self + + # Perform analysis before building, since this step will clobber build output + - template: /eng/pipelines/onebranch/steps/roslyn-analyzers-sqlclient-step.yml@self + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + sqlClientPackageVersion: '${{ parameters.sqlClientPackageVersion }}' + + # Perform the actual build + - template: /eng/pipelines/onebranch/steps/build-sqlclient-step.yml@self + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + sqlClientPackageVersion: '${{ parameters.sqlClientPackageVersion }}' + + # Sign the DLLs + - ${{ if parameters.isOfficial }}: + - template: /eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml@self + parameters: + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + # Minimatch pattern (multi-line, one pattern per line). + # **/ matches any nested directory (e.g. net8.0/, net462/fr/). + # + # Matches: net8.0/Microsoft.Data.SqlClient.dll + # net462/fr/Microsoft.Data.SqlClient.resources.dll + # Excludes: net8.0/Microsoft.Data.SqlClient.SNI.dll (no ** path matches) + pattern: | + **/Microsoft.Data.SqlClient.dll + **/Microsoft.Data.SqlClient.resources.dll + + # Copy DLLs to the assemblies/ subdirectory for APIScan. + - task: CopyFiles@2 + displayName: Copy DLLs for APIScan + inputs: + SourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/Package-Release' + # Matches: Microsoft.Data.SqlClient.dll (main assembly) + # Microsoft.Data.SqlClient.resources.dll (localized satellite assemblies in locale subdirs) + # Excludes: Microsoft.Data.SqlClient.SNI*.dll (native SNI — not part of this package) + Contents: | + **/Microsoft.Data.SqlClient.dll + **/Microsoft.Data.SqlClient.resources.dll + TargetFolder: '$(JOB_OUTPUT)/assemblies' + flattenFolders: false + + # Copy PDBs into the output directory so they are included in the published pipeline + # artifact. The symbols publishing stage will download this artifact and publish PDBs + # for this package using the files under symbols/. + - task: CopyFiles@2 + displayName: Copy PDBs for APIScan and Symbols Publishing + inputs: + SourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/Package-Release' + # Matches: Microsoft.Data.SqlClient.pdb (main assembly symbols) + # Excludes: Microsoft.Data.SqlClient.SNI*.pdb (native SNI — symbols published separately) + # Note: Resource DLLs are resource-only satellite assemblies and do not produce PDBs. + Contents: '**/Microsoft.Data.SqlClient.pdb' + TargetFolder: '$(JOB_OUTPUT)/symbols' + flattenFolders: false + + # Package the build output into a NuGet package + - template: /eng/pipelines/onebranch/steps/pack-sqlclient-step.yml@self + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + sqlClientPackageVersion: '${{ parameters.sqlClientPackageVersion }}' + + # Sign the NuGet packages + - ${{ if parameters.isOfficial }}: + - template: /eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml@self + parameters: + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + searchPath: '$(JOB_OUTPUT)/packages' + # Minimatch pattern with extglob. + # [0-9] matches a single digit, anchoring to the version segment so + # names like Microsoft.Data.SqlClient.SNI are not matched. + # ?(s) is an extglob that optionally matches 's'. + # + # Matches: Microsoft.Data.SqlClient.6.1.0.nupkg + # Microsoft.Data.SqlClient.6.1.0.snupkg + # Excludes: Microsoft.Data.SqlClient.SNI.6.0.0.nupkg + searchPattern: 'Microsoft.Data.SqlClient.[0-9]*.?(s)nupkg' diff --git a/eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml b/eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml index c04a2eceef..21d36b9640 100644 --- a/eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml +++ b/eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml @@ -38,15 +38,18 @@ parameters: # artifact. For example, if we're publishing the SqlClient package, and the build job publishes a # pipeline artifact with the following structure: # - # drop_BuildAndTest_PackageReference/ - # ├── Microsoft.Data.SqlClient.5.0.0.nupkg - # ├── Microsoft.Data.SqlClient.5.0.0.snupkg - # ├── Microsoft.Data.SqlClient.Extensions.Abstractions.1.0.0.nupkg - # └── other-file.txt + # drop_build_independent_build_logging/ + # ├── packages/ + # │ ├── Microsoft.Data.SqlClient.5.0.0.nupkg + # │ └── Microsoft.Data.SqlClient.5.0.0.snupkg + # └── symbols/ + # └── ... # - # Then the packagePath should be 'Microsoft.Data.SqlClient.5.0.0.nupkg'. + # Then the packagePath should be 'packages/Microsoft.Data.SqlClient.5.0.0.nupkg'. # - # Defaults to '${{ parameters.packageName }}.*.nupkg' to match any version of the package. + # Defaults to 'packages/${{ parameters.packageName }}.*.nupkg' to match any version of the + # package. Only .nupkg files are pushed; .snupkg files are included automatically by NuGet + # when they exist alongside the .nupkg in the same directory. # - name: packagePath type: string @@ -65,13 +68,13 @@ jobs: variables: - name: ob_outputDirectory - value: $(Build.SourcesDirectory)/output + value: $(JOB_OUTPUT) - name: artifactPath value: $(Pipeline.Workspace)/${{ parameters.artifactName }} - name: packageToPush - value: $(artifactPath)/${{ coalesce(parameters.packagePath, format('{0}.*.nupkg', parameters.packageName)) }} + value: $(artifactPath)/${{ coalesce(parameters.packagePath, format('packages/{0}.*.nupkg', parameters.packageName)) }} # Template context inputs are used to pass parameters to the deployment job since it doesn't # automatically download pipeline artifacts. diff --git a/eng/pipelines/onebranch/jobs/publish-symbols-job.yml b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml new file mode 100644 index 0000000000..21b20476cd --- /dev/null +++ b/eng/pipelines/onebranch/jobs/publish-symbols-job.yml @@ -0,0 +1,95 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# Reusable job template for publishing symbols for a single package. Downloads the pipeline +# artifact produced by a build job, then invokes publish-symbols-step.yml to upload and publish +# the PDB files to the internal and public symbol servers. +# +# Each package's PDBs are published separately to maintain unique naming and versioning. +# PDBs are expected to be located under the 'symbols/' directory within the downloaded +# artifact. + +parameters: + # The pipeline artifact name to download (OneBranch naming: drop__). + - name: artifactName + type: string + + # Short package name used in the job name (e.g. Logging, SqlServer, SqlClient). + - name: packageName + type: string + + # The full NuGet package name (e.g. Microsoft.Data.SqlClient.Internal.Logging). + - name: packageFullName + type: string + + # The package version, used for symbol versioning. + - name: packageVersion + type: string + + # Pattern to use to search for PDB files to upload/publish, relative to the symbols folder. + - name: searchPattern + type: string + + # Symbols Publishing Parameters ------------------------------------------ + + - name: symbolsAzureSubscription + type: string + + - name: symbolsPublishProjectName + type: string + + - name: symbolsPublishServer + type: string + + - name: symbolsPublishTokenUri + type: string + + - name: symbolsUploadAccount + type: string + +jobs: + - job: publish_symbols_${{ parameters.packageName }} + displayName: 'Publish Symbols: ${{ parameters.packageFullName }}' + pool: + type: windows + + variables: + # OneBranch requires ob_outputDirectory to be set, even if this job produces no artifacts. + ob_outputDirectory: $(JOB_OUTPUT) + + # Disable SDL scanning — this job only uploads/publishes PDBs and produces no + # assemblies to scan. APIScan and BinSkim are handled by the build jobs. + ob_sdl_apiscan_enabled: false + ob_sdl_binskim_break: false + ob_sdl_binskim_enabled: false + + # Path where the downloaded artifact will be placed. + artifactPath: $(Pipeline.Workspace)/${{ parameters.artifactName }} + + # Path to the PDB files within the downloaded artifact. + symbolsPath: $(Pipeline.Workspace)/${{ parameters.artifactName }}/symbols + + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download ${{ parameters.packageFullName }} Artifact' + inputs: + artifactName: '${{ parameters.artifactName }}' + targetPath: '$(artifactPath)' + + - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self + parameters: + artifactName: '${{ parameters.packageFullName }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.packageVersion }}_$(System.TimelineId)_$(System.JobAttempt)' + azureSubscription: '${{ parameters.symbolsAzureSubscription }}' + packageName: '${{ parameters.packageFullName }}' + publishProjectName: '${{ parameters.symbolsPublishProjectName }}' + publishServer: '${{ parameters.symbolsPublishServer }}' + publishToInternal: true + publishToPublic: true + publishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + searchPattern: '${{ parameters.searchPattern }}' + symbolsFolder: '$(symbolsPath)' + uploadAccount: '${{ parameters.symbolsUploadAccount }}' + version: '${{ parameters.packageVersion }}' diff --git a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml index 656ea8f181..a99b46d45f 100644 --- a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml @@ -4,278 +4,354 @@ # See the LICENSE file in the project root for more information. # ################################################################################# parameters: - - # The name of the pipeline artifacts to download prior to building the tests. + # The name of the pipeline artifact to download that contains the SqlClient NuGet package. - name: artifactName type: string - # True if this build is a preview. - - name: isPreview + # List of versions of dotnet that are *allowed to exist* in the NuGet package. Separators do not + # matter as the folders in lib, runtime, etc are simply checked to see if they exist in this + # string. + - name: expectedDotnetVersions + type: string + default: 'net462;net8.0;net9.0;netstandard2.0' + + # Expected file version of the assemblies within the package. This should be of the form: + # (major).(minor).(patch).(buildNumber) + - name: expectedFileVersion + type: string + + # List of folders that are *allowed to exist* in the NuGet package. Separators do not matter as + # the folders are simply checked to see if they exist in this string. + - name: expectedFolderNames + type: string + default: 'lib;ref;runtimes' + + # Expected NuGet package version. Used to build the installation path. This should be of the + # form: (major).(minor).(patch)[-preview(preview_number)] + - name: expectedPackageVersion + type: string + + # True if this build is an official build. This will be used to gate some checks + # that only apply to official builds, such as signature verification. + - name: isOfficial type: boolean jobs: -- job: validate_signed_package - displayName: 'Verify signed package' - - pool: - type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs - isCustom: true - name: ADO-1ES-Pool - vmImage: 'ADO-MMS22-SQL19' - - variables: # More settings at https://aka.ms/obpipelines/yaml/jobs - - template: /eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml@self - - - name: pathToDownloadedNuget # path to the downloaded nuget files - value: $(Pipeline.Workspace)\${{parameters.artifactName }} - - - ${{ if parameters.isPreview }}: - - name: extractedNugetPath - value: $(extractedNugetRootPath).$(mdsPackagePreviewVersion) - - name: mdsPackageVersion - value: $(mdsPackagePreviewVersion) - - steps: - - script: SET - displayName: 'Print Environment Variables' - - - task: NuGetToolInstaller@1 - displayName: 'Use NuGet' - - - powershell: | - # Displays the paths of all the local cache directories - nuget locals all -List - - #Clears all files from all local cache directories - nuget locals all -Clear - displayName: 'Clear local cache' - - - download: current - artifact: ${{parameters.artifactName}} - patterns: '**/*.*nupkg' - displayName: 'Download NuGet Package' - - - powershell: | - # Install nuget package - Install-Package -Name "Microsoft.Data.SqlClient" -Destination "$(TempFolderName)" -Force -Source $(pathToDownloadedNuget) -SkipDependencies - - Write-Host "--------------------------------------------------" - Write-Host '$(TempFolderName)' - ls $(TempFolderName) - Write-Host "--------------------------------------------------" - displayName: 'Extract Nuget in temp folder' - - - powershell: | - Write-Host "--------------------------------------------------" - Write-Host "This will verify the artifact signature" -ForegroundColor Green - Write-Host "--------------------------------------------------" - - nuget verify -All $(pathToDownloadedNuget)\*.nupkg - nuget verify -All $(pathToDownloadedNuget)\*.snupkg - displayName: 'Verify nuget signature' - - - powershell: | - # Recursively find all .dll files in TempFolder (installed nuget folder) - # Microsoft.Data.SqlClient.dll and Microsoft.Data.SqlClient.resources.dll (in localized folders) should have strong name - $dllFiles = Get-ChildItem -Path $(TempFolderName) -Recurse -Filter *.dll - $badDlls = @() - foreach ($file in $dllFiles) - { - # Run sn.exe to verify the strong name on each dll - $result = & "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" -vf $file.FullName - Write-OutPut $result - - # if the dll is not valid, it would be delay signed or test-signed which is not meant for production - if($result[$result.Length-1] -notlike "* is valid") - { - $badDlls += $result[$result.Length-1] - } - } - if($badDlls.Count -gt 0) - { - Write-OutPut "Error: Invalid dlls are detected. Check the list below:" - foreach($dll in $badDlls) - { - Write-Output $dll - } - Exit -1 - } - Write-Host "Strong name has been verified for all dlls" - displayName: 'Verify assembly strong names' - - - powershell: | - # Checks the expected folder names such as lib, ref, runtimes - Get-ChildItem -Path $(extractedNugetPath) -Directory | select Name | foreach { - if('$(expectedFolderNames)'.contains($_.Name)){ - Write-Host expected folder name verfied: $_.Name + - job: validate_nuget_package + displayName: "Validate NuGet package" + + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + isCustom: true + name: ADO-1ES-Pool + vmImage: "ADO-MMS22-SQL19" + + variables: # More settings at https://aka.ms/obpipelines/yaml/jobs + + # Path within the downloaded artifact where NuGet packages are located. + - name: packagesPath + value: '$(Pipeline.Workspace)\${{ parameters.artifactName }}\packages' + + # Path to the SqlClient NuGet package after installation. This path will only exist once the package + # been installed. + - name: nugetPackageInstallPath + value: '$(Pipeline.Workspace)\nugetPackageInstalls\Microsoft.Data.SqlClient.${{ parameters.expectedPackageVersion }}' + + # Root folder where NuGet package will be installed locally + - name: nugetPackageInstallRoot + value: '$(Pipeline.Workspace)\nugetPackageInstalls' + + steps: + - template: '/eng/pipelines/onebranch/steps/script-output-environment-variables-step.yml@self' + + - task: NuGetToolInstaller@1 + displayName: "Install NuGet" + + - powershell: | + echo "> 1. List all local cache directory paths" + nuget locals all -List + + echo "> 2. Clear all files from local cache directories" + nuget locals all -Clear + displayName: "Clear local cache" + + # Download NuGet packages from the specified build artifact. + - download: current + artifact: ${{ parameters.artifactName }} + patterns: "**/*.*nupkg" + displayName: "Download NuGet Package" + + # Verify secure signatures on the NuGet packages. + # NOTE: Packages will only be signed if the build is official. + - ${{ if eq(parameters.isOfficial, true) }}: + - powershell: | + # Propagate parameters to PS variables ####################### + $packagesPath = "${{ variables.packagesPath }}" + echo "packagesPath= $packagesPath" + + # Verify package signatures ################################## + echo "> 1. Verify signature of source package(s)" + nuget verify -All $packagesPath\*.nupkg + + echo "> 2. Verify signature of symbols package(s)" + nuget verify -All $packagesPath\*.snupkg + displayName: "Verify nuget signature" + + # Install NuGet package to the temporary directory + - powershell: | + # Propagate pipeline to PS variables ############################# + $packagesPath = "${{ variables.packagesPath }}" + echo "packagesPath= $packagesPath" + + $nugetPackageInstallRoot = "${{ variables.nugetPackageInstallRoot }}" + echo "nugetPackageInstallRoot= $nugetPackageInstallRoot" + + # Install NuGet Package ########################################## + echo "> 1. Installing Microsoft.Data.SqlClient NuGet package..." + Install-Package ` + -Name "Microsoft.Data.SqlClient" ` + -Source "$packagesPath" ` + -Destination $nugetPackageInstallRoot ` + -Force ` + -SkipDependencies + + echo "> 2. Listing contents of installed Microsoft.Data.SqlClient NuGet package:" + Write-Host $nugetPackageInstallRoot + Get-ChildItem $nugetPackageInstallRoot + Write-Host "$packagesPath" + Get-ChildItem "$packagesPath" + displayName: "Install NuGet Package" + + # Find all DLL files in the installed NuGet package, verify each is signed with a strong name + - powershell: | + # Propagate pipeline to PS variables ############################# + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" + + # Verify strong name signing ##################################### + echo "> 1. Verifying strong name signing of DLLs ..." + + # @TODO: This path seems brittle to VS upgrades, can we make it more flexible? + $snPath = "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" + + $dllFiles = Get-ChildItem -Path $nugetPackageInstallPath -Recurse -Filter *.dll + $badDlls = @() + foreach ($file in $dllFiles) + { + # Run sn.exe to verify the strong name on each dll + $result = & $snPath -vf $file.FullName + Write-OutPut $result + + # if the dll is not valid, it would be delay signed or test-signed which is not meant for production + if($result[$result.Length-1] -notlike "* is valid") + { + $badDlls += $result[$result.Length-1] + } } - } - displayName: 'Check expected folder names' - - - powershell: | - # Checks the version of DotNetFramework and DotNet - $countErr = 0 - $countPass = 0 - $excludNamesFromRuntimeFolder = 'lib','win','unix' - - Get-ChildItem -Path $(extractedNugetPath) -Directory | foreach { - $parentname=$_.Name - Write-Host $_.FullName -ForegroundColor yellow - - if($_.Name -ne 'runtimes') { - Get-ChildItem -Path $_.FullName -Directory | select Name | foreach { - if('$(expectedDotnetVersions)'.Contains($_.Name)){ - Write-Host "`tExpected version verified in $parentname": $_.Name -ForegroundColor green - $countPass += 1 + if($badDlls.Count -gt 0) + { + Write-OutPut "Error: Invalid dlls are detected. Check the list below:" + foreach($dll in $badDlls) + { + Write-Output $dll } - else{ - Write-Host "`tUnexpected version detected in $parentname": $_.Name - $countErr += 1 + Exit -1 + } + Write-Host "Strong name has been verified for all dlls" + displayName: "Verify assembly strong names" + + # Validate that the folders in the nuget are expected + # @TODO: This does not verify we are not missing any folders, only that the folders that + # exist are expected to exist. + - powershell: | + # Propagate pipeline to PS variables ############################# + $expectedFolderNames = "${{ parameters.expectedFolderNames }}" + echo "expectedFolderNames= $expectedFolderNames" + + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" + + # Verify folders are expected #################################### + Get-ChildItem -Path $nugetPackageInstallPath -Directory | select Name | foreach { + if($expectedFolderNames.contains($_.Name)){ + Write-Host expected folder name verfied: $_.Name } } - } + displayName: "Verify NuGet Root Folder Structure" + + # Validate that the folders within the root folders of the nuget are expected + # @TODO: This does not verify we are not missing any folders, only that the folders that + # exist are expected to exist. + - powershell: | + # Propagate pipeline to PS variables ############################# + $expectedDotnetVersions = "${{ parameters.expectedDotnetVersions }}" + echo "expectedDotnetVersions= $expectedDotnetVersions" + + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" + + # Verify folders are expected #################################### + # Checks the version of DotNetFramework and DotNet + $countErr = 0 + $countPass = 0 + $excludNamesFromRuntimeFolder = 'lib','win','unix' + + Get-ChildItem -Path $nugetPackageInstallPath -Directory | foreach { + $parentname=$_.Name + Write-Host $_.FullName -ForegroundColor yellow + + if($_.Name -ne 'runtimes') { + Get-ChildItem -Path $_.FullName -Directory | select Name | foreach { + if($expectedDotnetVersions.Contains($_.Name)){ + Write-Host "`tExpected version verified in $parentname": $_.Name -ForegroundColor green + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected in $parentname": $_.Name + $countErr += 1 + } + } + } - elseif ($_.Name -eq 'runtimes'){ - Get-ChildItem -Depth 3 -Path $_.FullName -Exclude $excludNamesFromRuntimeFolder -Directory | foreach{ - if('$(expectedDotnetVersions)'.Contains($_.Name)){ - Write-Host "`tExpected version verfied in $parentname": $_.Name - $countPass += 1 + elseif ($_.Name -eq 'runtimes'){ + Get-ChildItem -Depth 3 -Path $_.FullName -Exclude $excludNamesFromRuntimeFolder -Directory | foreach{ + if('${{ parameters.expectedDotnetVersions }}'.Contains($_.Name)){ + Write-Host "`tExpected version verfied in $parentname": $_.Name + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected": $_.Name -ForegroundColor Red + $countErr += 1 + } + } } else{ - Write-Host "`tUnexpected version detected": $_.Name -ForegroundColor Red - $countErr += 1 + Write-Host "`tUnknown folder " $_.Name -ForegroundColor Red + Exit -1 } } - } - else{ - Write-Host "`tUnknown folder " $_.Name -ForegroundColor Red - Exit -1 - } - } - - Write-Host "_______________" - Write-Host "Expected: $countPass" - Write-Host "Unexpected: $countErr" - Write-Host "_______________" - if ($countErr -ne 0) - { - Write-Host "Unexpected versions are detected!" -ForegroundColor Red - Exit -1 - } - displayName: 'Check Expected framework' - - - powershell: | - # list all the child items of created temp folder - - #Verify all DLLs unzipped match "expected" hierarchy - - foreach( $folderName in (Get-ChildItem -Path $(extractedNugetPath) -Directory).Name) - { - # List all Childerns of the Path - Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File - $subFiles = Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File - - foreach($file in $subFiles) - { - if($subFiles[0].Name -like "*.dll" ) + + Write-Host "_______________" + Write-Host "Expected: $countPass" + Write-Host "Unexpected: $countErr" + Write-Host "_______________" + if ($countErr -ne 0) { - Write-Host $subFiles[0].Name -ForegroundColor Green - Write-Host $subFiles[1].Name -ForegroundColor Green - if(($folderName -eq 'lib') -or ($folderName -eq 'ref')) + Write-Host "Unexpected versions are detected!" -ForegroundColor Red + Exit -1 + } + displayName: "Verify NuGet DotNet Versions " + + - powershell: | + # Propagate pipeline to PS variables ############################# + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" + + # Verify DLL Hierarchy ########################################### + foreach( $folderName in (Get-ChildItem -Path $nugetPackageInstallPath -Directory).Name) + { + # List all Childerns of the Path + Get-ChildItem -Path $nugetPackageInstallPath\$folderName -Recurse -File + $subFiles = Get-ChildItem -Path $nugetPackageInstallPath\$folderName -Recurse -File + + foreach($file in $subFiles) { - if($subFiles[2].Name -like "*.dll") + if($subFiles[0].Name -like "*.dll" ) { - Write-Host $subFiles[2].Name -ForegroundColor Green + Write-Host $subFiles[0].Name -ForegroundColor Green + Write-Host $subFiles[1].Name -ForegroundColor Green + if(($folderName -eq 'lib') -or ($folderName -eq 'ref')) + { + if($subFiles[2].Name -like "*.dll") + { + Write-Host $subFiles[2].Name -ForegroundColor Green + } + else + { + $subFiles[2].Name + Write-Host "Expected file pattern for localization did not match to *.dll" -ForegroundColor Red + Exit -1 + } + } } else { - $subFiles[2].Name - Write-Host "Expected file pattern for localization did not match to *.dll" -ForegroundColor Red + $subFiles[0].Name + $subFiles[1].Name + Write-Host "Expected file pattern did not match to *.dll" -ForegroundColor Red Exit -1 } } } - else - { - $subFiles[0].Name - $subFiles[1].Name - Write-Host "Expected file pattern did not match to *.dll" -ForegroundColor Red - Exit -1 - } - } - } - displayName: 'Verify all DLLs unzipped match "expected" hierarchy' - - powershell: | - # Verify all dlls status are Valid - - $dlls = Get-ChildItem -Path $(extractedNugetPath) -Recurse -Include *.dll - foreach ($status in $dlls | Get-AuthenticodeSignature) - { - if ($status.Status -eq "Valid") - { - Write-Host $status.Status $status.Path - } - else - { - Write-Host "dll status of '$status.Path' is not valid!" -ForegroundColor Red - $status - Exit -1 - } - } - displayName: 'Verify all dlls status are Valid' + displayName: 'Verify all DLLs unzipped match "expected" hierarchy' + + # Verify that all DLLs are authenticode signed + # NOTE: This signing is only performed on official builds. + - ${{ if eq(parameters.isOfficial, true) }}: + - powershell: | + # Propagate pipeline to PS variables ############################# + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" + + # Verify authenticode signature of DLLs ########################## + $dlls = Get-ChildItem -Path $nugetPackageInstallPath -Recurse -Include *.dll + foreach ($status in $dlls | Get-AuthenticodeSignature) + { + if ($status.Status -eq "Valid") + { + Write-Host $status.Status $status.Path + } + else + { + Write-Host "dll status of '$status.Path' is not valid!" -ForegroundColor Red + $status + Exit -1 + } + } + displayName: "Verify all dlls status are Valid" - - powershell: | - # This will check each DLL's ProductVersion and FileVersion against - # expected values. - $failed = 0 + - powershell: | + # Propagate pipeline to PS variables ############################# + $expectedFileVersion = "${{ parameters.expectedFileVersion }}" + echo "expectedFileVersion= $expectedFileVersion" - foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo ) - { - if ($pVersion.ProductVersion -Like '$(mdsPackageVersion)*') - { - Write-Host -ForegroundColor Green "Correct ProductVersion detected for $($pVersion.FileName): $($pVersion.ProductVersion)" - } - else - { - Write-Host -ForegroundColor Red "Wrong ProductVersion detected for $($pVersion.FileName); expected: $(mdsPackageVersion); found: $($pVersion.ProductVersion)" - $failed = 1 - } + $expectedPackageVersion = "${{ parameters.expectedPackageVersion }}" + echo "expectedPackageVersion= $expectedPackageVersion" + + $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" + echo "nugetPackageInstallPath= $nugetPackageInstallPath" - if ($pVersion.FileVersion -eq '$(mdsAssemblyFileVersion)') + # Validate ProductVersion and FileVersion fields ################# + $failed = 0 + foreach ( $pVersion in Get-ChildItem *.dll -Path $nugetPackageInstallPath -Recurse | ForEach-Object versioninfo ) { - Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)" + if ($pVersion.ProductVersion -Like $expectedPackageVersion + '*') + { + Write-Host -ForegroundColor Green "Correct ProductVersion detected for $($pVersion.FileName): $($pVersion.ProductVersion)" + } + else + { + Write-Host -ForegroundColor Red "Wrong ProductVersion detected for $($pVersion.FileName); expected: $expectedPackageVersion; found: $($pVersion.ProductVersion)" + $failed = 1 + } + + if ($pVersion.FileVersion -eq $expectedFileVersion) + { + Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)" + } + else + { + Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $expectedFileVersion; found: $($pVersion.FileVersion)" + $failed = 1 + } + + # @TODO: We should do a check for assembly version here. } - else + + if ($failed -ne 0) { - Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $(mdsAssemblyFileVersion); found: $($pVersion.FileVersion)" - $failed = 1 + Exit -1 } - } - - if ($failed -ne 0) - { - Exit -1 - } - - Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List - displayName: 'Verify "File Version" matches expected values for DLLs' - - - powershell: | - # Check assembly versions. - # - # GOTCHA: This expects the Versions.props file having XML elements in a - # certain order. If the order changes, this check will fail! - # - # TODO: This also isn't checking the versions of the actual assemblies in - # the package, so it isn't terribly useful. - - [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" - $AssemblyFileVersion = $versionprops.Project.PropertyGroup[2].AssemblyFileVersion - $AssemblyVersion = $versionprops.Project.PropertyGroup[2].AssemblyVersion - - if($AssemblyFileVersion -eq $AssemblyVersion) - { - Write-Host AssemblyFileVersion: $AssemblyFileVersion should not be equal to: $AssemblyVersion - Exit -1 - } - displayName: 'Check "AssemblyFileVersion" is not same as "AssemblyVersion" in version.props' + + Get-ChildItem *.dll -Path $nugetPackageInstallPath -Recurse | ForEach-Object VersionInfo | Format-List + displayName: 'Verify "File Version" matches expected values for DLLs' diff --git a/eng/pipelines/onebranch/scripts/publish-symbols.ps1 b/eng/pipelines/onebranch/scripts/publish-symbols.ps1 new file mode 100644 index 0000000000..aeaa665a92 --- /dev/null +++ b/eng/pipelines/onebranch/scripts/publish-symbols.ps1 @@ -0,0 +1,236 @@ +<# +.SYNOPSIS + Publishes symbols to the Microsoft symbol publishing service (SymWeb/MSDL). + +.DESCRIPTION + This script is Step 2 of the two-step symbols publishing process. It requests + the Symbols Publishing Pipeline to publish previously uploaded PDB files to + internal (SymWeb) and/or public (MSDL) Microsoft symbol servers. + + The two-step process: + Step 1 (PublishSymbols@2 task in publish-symbols-step.yml): + Uploads PDB files to the Azure DevOps symbol store under a unique + artifact name (SymbolsArtifactName). This stores the symbols but does + NOT make them available on SymWeb or MSDL. + + Step 2 (this script): + Calls the Symbols Publishing Pipeline REST API to request that the + uploaded symbols be published to the symbol servers. + + Step 2 depends on Step 1: the -ArtifactName parameter MUST match the + SymbolsArtifactName used by the PublishSymbols@2 upload task so that + both steps reference the same uploaded artifact. + + This script performs four sub-steps: + 1. Acquires a bearer token from Azure CLI for the symbol publishing service. + 2. Registers a unique request name with the publishing service. + 3. Submits the request to publish symbols to the specified servers. + 4. Queries the publishing status for confirmation. + + For more details on the Symbols Publishing Pipeline, see: + https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL + +.PARAMETER PublishServer + The hostname prefix of the symbol publishing service. This value is prepended to + '.trafficmanager.net' to construct the service base URL. + +.PARAMETER PublishTokenUri + The resource URI used to acquire a bearer token from Azure CLI + (via 'az account get-access-token --resource '). + +.PARAMETER PublishProjectName + The project name registered with the symbol publishing service (decided during onboarding). + +.PARAMETER ArtifactName + The name of the publishing request. This must match the SymbolsArtifactName used by + the PublishSymbols@2 upload task so that upload and publish reference the same artifact. + +.PARAMETER PublishToInternal + Whether to publish symbols to the internal symbol server. Defaults to $true. + +.PARAMETER PublishToPublic + Whether to publish symbols to the public symbol server. Defaults to $true. + +.EXAMPLE + .\publish-symbols.ps1 ` + -PublishServer "mysymbolserver" ` + -PublishTokenUri "https://login.microsoftonline.com/..." ` + -PublishProjectName "Microsoft.Data.SqlClient.SNI" ` + -ArtifactName "mds_symbols_MyProject_dotnet-sqlclient_main_7.0.0_abc123_1" + + Publishes symbols to both internal and public servers using the specified parameters. + +.EXAMPLE + .\publish-symbols.ps1 ` + -PublishServer "mysymbolserver" ` + -PublishTokenUri "https://login.microsoftonline.com/..." ` + -PublishProjectName "Microsoft.Data.SqlClient.SNI" ` + -ArtifactName "mds_symbols_MyProject_dotnet-sqlclient_main_7.0.0_abc123_2" ` + -PublishToPublic $false + + Publishes symbols to the internal server only (retry attempt 2). + +.NOTES + File Name : publish-symbols.ps1 + Requires : Azure CLI (az) must be installed and authenticated. + Called by : publish-symbols-step.yml (Azure Pipelines template) + + Publishing status codes returned by the service: + + PublishingStatus: + 0 - NotRequested: The request has not been requested to publish. + 1 - Submitted: The request is submitted to be published. + 2 - Processing: The request is still being processed. + 3 - Completed: Processing finished. Check PublishingResult for details. + + PublishingResult: + 0 - Pending: The request has not completed or has not been requested. + 1 - Succeeded: The request published successfully. + 2 - Failed: The request failed to publish. + 3 - Cancelled: The request was cancelled. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, HelpMessage = "Hostname prefix of the symbol publishing service (prepended to .trafficmanager.net).")] + [ValidateNotNullOrEmpty()] + [string]$PublishServer, + + [Parameter(Mandatory = $true, HelpMessage = "Resource URI for acquiring a bearer token via Azure CLI.")] + [ValidateNotNullOrEmpty()] + [string]$PublishTokenUri, + + [Parameter(Mandatory = $true, HelpMessage = "Project name registered with the symbol publishing service.")] + [ValidateNotNullOrEmpty()] + [string]$PublishProjectName, + + [Parameter(Mandatory = $true, HelpMessage = "Artifact name for the publishing request (must match PublishSymbols@2 SymbolsArtifactName).")] + [ValidateNotNullOrEmpty()] + [string]$ArtifactName, + + [Parameter(Mandatory = $false, HelpMessage = "Publish symbols to the internal symbol server.")] + [bool]$PublishToInternal = $true, + + [Parameter(Mandatory = $false, HelpMessage = "Publish symbols to the public symbol server.")] + [bool]$PublishToPublic = $true +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +# --- Log input parameters --- +Write-Host "=== Publish Symbols Parameters ===" +Write-Host "PublishServer: ${PublishServer}" +Write-Host "PublishTokenUri: ${PublishTokenUri}" +Write-Host "PublishProjectName: ${PublishProjectName}" +Write-Host "ArtifactName: ${ArtifactName}" +Write-Host "PublishToInternal: ${PublishToInternal}" +Write-Host "PublishToPublic: ${PublishToPublic}" +Write-Host "==================================" + +# --- Build request name and URLs --- +$requestName = ${ArtifactName} +$baseUrl = "https://${PublishServer}.trafficmanager.net/projects/${PublishProjectName}" +$registerUrl = "${baseUrl}/requests" +$requestUrl = "${baseUrl}/requests/${requestName}" + +Write-Host "=== Constructed URLs ===" +Write-Host "Request Name: ${requestName}" +Write-Host "Base URL: ${baseUrl}" +Write-Host "Register URL: ${registerUrl}" +Write-Host "Request URL: ${requestUrl}" +Write-Host "========================" + +# --- Step 1: Acquire token --- +Write-Host "> 1. Acquiring symbol publishing token..." +$symbolPublishingToken = az account get-access-token --resource ${PublishTokenUri} --query accessToken -o tsv +if ($LASTEXITCODE -ne 0) { + throw "Failed to acquire symbol publishing token via Azure CLI (exit code: ${LASTEXITCODE})." +} +if ($null -ne $symbolPublishingToken) { + $symbolPublishingToken = $symbolPublishingToken.Trim() +} +if ([string]::IsNullOrWhiteSpace($symbolPublishingToken)) { + throw "Failed to acquire symbol publishing token via Azure CLI: received an empty or whitespace-only access token." +} +Write-Host "> 1. Symbol publishing token acquired." + +$authHeaders = @{ Authorization = "Bearer ${symbolPublishingToken}" } + +# --- Step 2: Register request name --- +Write-Host "> 2. Registering request name..." +$requestNameRegistrationBody = @{ requestName = $requestName } | ConvertTo-Json -Compress +try { + Invoke-RestMethod -Method POST -Uri ${registerUrl} -Headers ${authHeaders} -ContentType "application/json" -Body ${requestNameRegistrationBody} +} catch { + throw "Failed to register request name. URI: ${registerUrl} | Body: ${requestNameRegistrationBody} | Error: $_" +} +Write-Host "> 2. Request name registered successfully." + +# --- Step 3: Publish symbols --- +Write-Host "> 3. Submitting request to publish symbols..." +$publishSymbolsBody = @{ + publishToInternalServer = $PublishToInternal + publishToPublicServer = $PublishToPublic +} | ConvertTo-Json -Compress +Write-Host "Publishing symbols request body: ${publishSymbolsBody}" +try { + Invoke-RestMethod -Method POST -Uri ${requestUrl} -Headers ${authHeaders} -ContentType "application/json" -Body ${publishSymbolsBody} +} catch { + throw "Failed to publish symbols. URI: ${requestUrl} | Body: ${publishSymbolsBody} | Error: $_" +} +Write-Host "> 3. Request to publish symbols submitted successfully." + +# --- Step 4: Check status --- +Write-Host "> 4. Checking the status of the request..." +try { + $status = Invoke-RestMethod -Method GET -Uri ${requestUrl} -Headers ${authHeaders} -ContentType "application/json" + $status +} catch { + throw "Failed to check request status. URI: ${requestUrl} | Error: $_" +} + +# Validate publishing results — fail the task when the service reports a terminal failure. +# PublishingResult: 0=Pending, 1=Succeeded, 2=Failed, 3=Cancelled +$resultLabels = @{ 0 = 'Pending'; 1 = 'Succeeded'; 2 = 'Failed'; 3 = 'Cancelled' } +$failures = @() + +if ($PublishToInternal) { + $internalResult = $status.publishToInternalServerResult + if ($null -ne $internalResult -and $internalResult -ge 2) { + $label = if ($resultLabels.ContainsKey([int]$internalResult)) { $resultLabels[[int]$internalResult] } else { "Unknown($internalResult)" } + $failures += "Internal server publishing result: ${label} (${internalResult})" + } +} + +if ($PublishToPublic) { + $publicResult = $status.publishToPublicServerResult + if ($null -ne $publicResult -and $publicResult -ge 2) { + $label = if ($resultLabels.ContainsKey([int]$publicResult)) { $resultLabels[[int]$publicResult] } else { "Unknown($publicResult)" } + $failures += "Public server publishing result: ${label} (${publicResult})" + } +} + +if ($failures.Count -gt 0) { + $failureMessage = $failures -join '; ' + throw "Symbol publishing reported a terminal failure. ${failureMessage}. URI: ${requestUrl}" +} + +Write-Host "> 4. Status check completed - no terminal failures detected." + +Write-Host "" +Write-Host "Use below tables to interpret the xxxServerStatus and xxxServerResult fields from the response." +Write-Host "" +Write-Host "PublishingStatus" +Write-Host "-----------------" +Write-Host "0 NotRequested - The request has not been requested to publish." +Write-Host "1 Submitted - The request is submitted to be published." +Write-Host "2 Processing - The request is still being processed." +Write-Host "3 Completed - Processing finished. Check PublishingResult for details." +Write-Host "" +Write-Host "PublishingResult" +Write-Host "-----------------" +Write-Host "0 Pending - The request has not completed or has not been requested." +Write-Host "1 Succeeded - The request published successfully." +Write-Host "2 Failed - The request failed to publish." +Write-Host "3 Cancelled - The request was cancelled." diff --git a/eng/pipelines/onebranch/scripts/tests/README.md b/eng/pipelines/onebranch/scripts/tests/README.md new file mode 100644 index 0000000000..447dda5d32 --- /dev/null +++ b/eng/pipelines/onebranch/scripts/tests/README.md @@ -0,0 +1,43 @@ +# Publish-Symbols Tests + +Pester tests for the `publish-symbols.ps1` script used by the symbol publishing pipeline step. + +## Prerequisites + +- PowerShell 5.1+ or PowerShell 7+ +- [Pester v5](https://pester.dev/) (`Install-Module Pester -MinimumVersion 5.0 -Scope CurrentUser`) + +## Running the Tests + +From this directory: + +```powershell +Invoke-Pester ./publish-symbols.Tests.ps1 +``` + +Or from the repository root: + +```powershell +Invoke-Pester ./eng/pipelines/onebranch/scripts/tests/ +``` + +For detailed output: + +```powershell +Invoke-Pester ./publish-symbols.Tests.ps1 -Output Detailed +``` + +## Test Coverage + +| Area | What's tested | +| --------------------- | ---------------------------------------------------------------- | +| Parameter validation | Empty strings rejected for all mandatory parameters | +| URL construction | Base URL, register URL, request URL built from parameters | +| Request bodies | Registration body, default publish flags, flag overrides | +| Error handling | Token failure, registration failure, publish failure, status failure — all verify expanded URI in error message | +| Status validation | Detects Failed/Cancelled results, respects PublishToInternal/PublishToPublic flags, passes on Succeeded/Pending | + +## Notes + +- All external calls (`az`, `Invoke-RestMethod`) are mocked — no network access or Azure credentials are required. +- Tests validate the script at `../publish-symbols.ps1` relative to this directory. diff --git a/eng/pipelines/onebranch/scripts/tests/publish-symbols.Tests.ps1 b/eng/pipelines/onebranch/scripts/tests/publish-symbols.Tests.ps1 new file mode 100644 index 0000000000..4d16d54e96 --- /dev/null +++ b/eng/pipelines/onebranch/scripts/tests/publish-symbols.Tests.ps1 @@ -0,0 +1,356 @@ +<# +.SYNOPSIS + Pester tests for publish-symbols.ps1 +#> + +BeforeAll { + $scriptPath = Join-Path $PSScriptRoot '..' 'publish-symbols.ps1' +} + +AfterAll { + # Clean up global variables used by mocks + Remove-Variable -Name 'restCalls' -Scope Global -ErrorAction SilentlyContinue + Remove-Variable -Name 'mockCallCount' -Scope Global -ErrorAction SilentlyContinue +} + +Describe 'publish-symbols.ps1 Parameter Validation' { + + It 'Should reject an empty PublishServer' { + { & $scriptPath -PublishServer '' -PublishTokenUri 'https://token' -PublishProjectName 'proj' -ArtifactName 'art' } | + Should -Throw + } + + It 'Should reject an empty PublishTokenUri' { + { & $scriptPath -PublishServer 'server' -PublishTokenUri '' -PublishProjectName 'proj' -ArtifactName 'art' } | + Should -Throw + } + + It 'Should reject an empty PublishProjectName' { + { & $scriptPath -PublishServer 'server' -PublishTokenUri 'https://token' -PublishProjectName '' -ArtifactName 'art' } | + Should -Throw + } + + It 'Should reject an empty ArtifactName' { + { & $scriptPath -PublishServer 'server' -PublishTokenUri 'https://token' -PublishProjectName 'proj' -ArtifactName '' } | + Should -Throw + } +} + +Describe 'publish-symbols.ps1 URL Construction' { + + BeforeAll { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token-12345' } + + $global:restCalls = @() + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:restCalls += @{ + Method = $Method + Uri = $Uri + Body = $Body + } + return @{ publishToInternalServerStatus = 0; publishToPublicServerStatus = 0; publishToInternalServerResult = 0; publishToPublicServerResult = 0 } + } + } + + BeforeEach { + $global:restCalls = @() + } + + It 'Should construct the correct base URL from PublishServer and PublishProjectName' { + & $scriptPath ` + -PublishServer 'myserver' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'My.Project' ` + -ArtifactName 'test_artifact' + + $global:restCalls.Count | Should -Be 3 + + # Registration call + $global:restCalls[0].Uri | Should -Be 'https://myserver.trafficmanager.net/projects/My.Project/requests' + $global:restCalls[0].Method | Should -Be 'POST' + + # Publish call + $global:restCalls[1].Uri | Should -Be 'https://myserver.trafficmanager.net/projects/My.Project/requests/test_artifact' + $global:restCalls[1].Method | Should -Be 'POST' + + # Status call + $global:restCalls[2].Uri | Should -Be 'https://myserver.trafficmanager.net/projects/My.Project/requests/test_artifact' + $global:restCalls[2].Method | Should -Be 'GET' + } + + It 'Should use ArtifactName directly as the request name' { + & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'myartifact_3' + + $global:restCalls[1].Uri | Should -BeLike '*myartifact_3' + $global:restCalls[2].Uri | Should -BeLike '*myartifact_3' + } +} + +Describe 'publish-symbols.ps1 Request Bodies' { + + BeforeAll { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token-12345' } + + $global:restCalls = @() + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:restCalls += @{ + Method = $Method + Uri = $Uri + Body = $Body + } + return @{ publishToInternalServerStatus = 0; publishToPublicServerStatus = 0; publishToInternalServerResult = 0; publishToPublicServerResult = 0 } + } + } + + BeforeEach { + $global:restCalls = @() + } + + It 'Should send the correct request name in the registration body' { + & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'my_artifact_1' + + $body = $global:restCalls[0].Body | ConvertFrom-Json + $body.requestName | Should -Be 'my_artifact_1' + } + + It 'Should default to publishing to both internal and public servers' { + & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' + + $body = $global:restCalls[1].Body | ConvertFrom-Json + $body.publishToInternalServer | Should -Be $true + $body.publishToPublicServer | Should -Be $true + } + + It 'Should respect PublishToInternal=false' { + & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' ` + -PublishToInternal $false + + $body = $global:restCalls[1].Body | ConvertFrom-Json + $body.publishToInternalServer | Should -Be $false + $body.publishToPublicServer | Should -Be $true + } + + It 'Should respect PublishToPublic=false' { + & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' ` + -PublishToPublic $false + + $body = $global:restCalls[1].Body | ConvertFrom-Json + $body.publishToInternalServer | Should -Be $true + $body.publishToPublicServer | Should -Be $false + } +} + +Describe 'publish-symbols.ps1 Error Handling' { + + It 'Should throw when token acquisition fails' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 1; return '' } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*token*' + } + + It 'Should throw when token is empty' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return ' ' } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*empty*' + } + + It 'Should throw with URI details when registration fails' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + Mock -CommandName 'Invoke-RestMethod' -MockWith { throw "Connection refused" } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*Failed to register*' + } + + It 'Should throw with URI details when publish fails' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -eq 1) { return @{} } + throw "Service unavailable" + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*Failed to publish*' + } + + It 'Should throw with URI details when status check fails' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + throw "Timeout" + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*Failed to check*' + } +} + +Describe 'publish-symbols.ps1 Status Failure Detection' { + + It 'Should throw when internal server result is Failed (2)' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 2; publishToPublicServerResult = 0 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*terminal failure*Internal server*Failed*' + } + + It 'Should throw when public server result is Cancelled (3)' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 1; publishToPublicServerResult = 3 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*terminal failure*Public server*Cancelled*' + } + + It 'Should throw when both servers report failure' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 2; publishToPublicServerResult = 3 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Throw '*terminal failure*Internal server*Public server*' + } + + It 'Should not throw when both servers report Succeeded (1)' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 1; publishToPublicServerResult = 1 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Not -Throw + } + + It 'Should not throw when results are Pending (0)' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 0; publishToPublicServerResult = 0 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' } | + Should -Not -Throw + } + + It 'Should not check internal result when PublishToInternal is false' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 2; publishToPublicServerResult = 1 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' ` + -PublishToInternal $false } | + Should -Not -Throw + } + + It 'Should not check public result when PublishToPublic is false' { + Mock -CommandName 'az' -MockWith { $global:LASTEXITCODE = 0; return 'fake-token' } + $global:mockCallCount = 0 + Mock -CommandName 'Invoke-RestMethod' -MockWith { + $global:mockCallCount++ + if ($global:mockCallCount -le 2) { return @{} } + return @{ publishToInternalServerResult = 1; publishToPublicServerResult = 2 } + } + + { & $scriptPath ` + -PublishServer 'srv' ` + -PublishTokenUri 'https://token-uri' ` + -PublishProjectName 'proj' ` + -ArtifactName 'art' ` + -PublishToPublic $false } | + Should -Not -Throw + } +} diff --git a/eng/pipelines/onebranch/sqlclient-non-official.yml b/eng/pipelines/onebranch/sqlclient-non-official.yml index 2274d8cccf..4ae301020f 100644 --- a/eng/pipelines/onebranch/sqlclient-non-official.yml +++ b/eng/pipelines/onebranch/sqlclient-non-official.yml @@ -45,7 +45,7 @@ parameters: default: true # Build the Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider package. - - name: buildAKVProvider + - name: buildAkvProvider displayName: Build Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider type: boolean default: true @@ -84,41 +84,17 @@ parameters: default: false # Release the Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider package. - - name: releaseAKVProvider + - name: releaseAkvProvider displayName: Release Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider type: boolean default: false variables: + - template: /eng/pipelines/onebranch/variables/common-variables.yml@self - template: /eng/pipelines/onebranch/variables/onebranch-variables.yml@self - - # Define the effective versions for all of the packages we build and release. - - ${{ if parameters.isPreview }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackagePreviewVersion) - - name: effectiveLoggingVersion - value: $(loggingPackagePreviewVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackagePreviewVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackagePreviewVersion) - - name: effectiveAzureVersion - value: $(azurePackagePreviewVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackagePreviewVersion) - - ${{ else }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackageVersion) - - name: effectiveLoggingVersion - value: $(loggingPackageVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackageVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackageVersion) - - name: effectiveAzureVersion - value: $(azurePackageVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackageVersion) + - template: /eng/pipelines/onebranch/variables/package-variables.yml@self + parameters: + isPreview: ${{ parameters.isPreview }} resources: repositories: @@ -130,63 +106,130 @@ resources: extends: # See: https://aka.ms/obpipelines/templates template: /v2/OneBranch.NonOfficial.CrossPlat.yml@templates + parameters: - release: - # This indicates the pipeline category to deploy Box products. See: - # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts - category: NonAzure featureFlags: EnableCDPxPAT: false WindowsHostVersion: 1ESWindows2022 + + release: + # This indicates the pipeline category to deploy Box products. See: + # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts + category: NonAzure + # See: https://aka.ms/obpipelines/sdl globalSdl: - tsa: - # We disable TSA for non-official pipelines. This inhibits spurious TSA alerts and ADO - # task creation, since non-official builds are often done against development branches. - enabled: false apiscan: + # APIScan "software name" and version parameters are set on a per-job basis with via + # ob_sdl_apiscan_* variables. enabled: true break: false - # Other ApiScan options are set by the jobs via ob_sdl_apiscan_* variables. + + armory: + enabled: true + + asyncSdl: + # Disabling this as it complicates the build process with minimal gain + enabled: false + + binskim: + enabled: true + codeql: - compiled: enabled: true - sbom: + compiled: + + credscan: enabled: true - packageName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + suppressionsFile: '$(REPO_ROOT)/.config/CredScanSuppressions.json' + + eslint: + # Only useful for repos with ECMAscript - which we do not have. + enabled: false + policheck: enabled: true - break: true - exclusionsFile: $(REPO_ROOT)\.config\PolicheckExclusions.xml - asyncSdl: + exclusionsFile: '$(REPO_ROOT)\.config\PolicheckExclusions.xml' + + roslyn: + # Note, requires RoslynAnalyzers task to be added as a separate step + enabled: true + + publishLogs: + enabled: true + + sbom: + enabled: true + packageName: 'Microsoft.Data.SqlClient' + packageVersion: '$(Build.BuildNumber)' + + tsa: + # We disable TSA for non-official pipelines. This inhibits spurious TSA alerts and ADO task + # creation, since non-official builds are often done against development branches. This also + # forces all SDL tasks into "break" build mode. enabled: false - credscan: - enabled: true - suppressionsFile: $(REPO_ROOT)/.config/CredScanSuppressions.json - binskim: - enabled: true - armory: - enabled: true - break: true - eslint: - enabled: false - roslyn: - enabled: true - break: true - publishLogs: - enabled: true - tsaOptionsPath: $(REPO_ROOT)\.config\tsaoptions.json - disableLegacyManifest: true + configFile: '$(REPO_ROOT)/.config/tsaoptions.json' + stages: - template: /eng/pipelines/onebranch/stages/build-stages.yml@self parameters: - debug: ${{ parameters.debug }} - isPreview: ${{ parameters.isPreview }} + isOfficial: false # This is a non-official pipeline. + buildSqlServerServer: ${{ parameters.buildSqlServerServer }} + buildSqlClient: ${{ parameters.buildSqlClient }} + buildAkvProvider: ${{ parameters.buildAkvProvider }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsFileVersion: '${{ variables.abstractionsFileVersion }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderFileVersion: '${{ variables.akvProviderFileVersion }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azureFileVersion: '${{ variables.azureFileVersion }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingFileVersion: '${{ variables.loggingFileVersion }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientFileVersion: '${{ variables.sqlClientFileVersion }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerFileVersion: '${{ variables.sqlServerFileVersion }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + + signingAppRegistrationClientId: '$(SigningAppRegistrationClientId)' + signingAppRegistrationTenantId: '$(SigningAppRegistrationTenantId)' + signingAuthAkvName: '$(SigningAuthAkvName)' + signingAuthSignCertName: '$(SigningAuthSignCertName)' + signingEsrpClientId: '$(SigningEsrpClientId)' + signingEsrpConnectedServiceName: '$(SigningEsrpConnectedServiceName)' + + - template: /eng/pipelines/onebranch/stages/publish-symbols-stage.yml@self + parameters: publishSymbols: ${{ parameters.publishSymbols }} buildSqlServerServer: ${{ parameters.buildSqlServerServer }} buildSqlClient: ${{ parameters.buildSqlClient }} - buildAKVProvider: ${{ parameters.buildAKVProvider }} + buildAkvProvider: ${{ parameters.buildAkvProvider }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + + symbolsAzureSubscription: '$(SymbolsAzureSubscription)' + symbolsPublishProjectName: '$(SymbolsPublishProjectNameSqlClient)' + # Non-Official pipelines must publish to the PPE symbol server. + symbolsPublishServer: '$(SymbolsPublishServerPPE)' + symbolsPublishTokenUri: '$(SymbolsPublishTokenUriPPE)' + symbolsUploadAccount: '$(SymbolsUploadAccount)' - template: /eng/pipelines/onebranch/stages/release-stages.yml@self parameters: @@ -196,9 +239,25 @@ extends: # This is _not_ an official pipeline. isOfficial: false stageNameSuffix: test + + publishSymbols: ${{ parameters.publishSymbols }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + releaseSqlServerServer: ${{ parameters.releaseSqlServerServer }} releaseLogging: ${{ parameters.releaseLogging }} releaseAbstractions: ${{ parameters.releaseAbstractions }} releaseSqlClient: ${{ parameters.releaseSqlClient }} releaseAzure: ${{ parameters.releaseAzure }} - releaseAKVProvider: ${{ parameters.releaseAKVProvider }} + releaseAkvProvider: ${{ parameters.releaseAkvProvider }} diff --git a/eng/pipelines/onebranch/sqlclient-official.yml b/eng/pipelines/onebranch/sqlclient-official.yml index c9548b25d6..a2902f1e21 100644 --- a/eng/pipelines/onebranch/sqlclient-official.yml +++ b/eng/pipelines/onebranch/sqlclient-official.yml @@ -66,7 +66,7 @@ parameters: default: true # Build the Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider package. - - name: buildAKVProvider + - name: buildAkvProvider displayName: Build Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider type: boolean default: true @@ -105,41 +105,17 @@ parameters: default: false # Release the Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider package. - - name: releaseAKVProvider + - name: releaseAkvProvider displayName: Release Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider type: boolean default: false variables: + - template: /eng/pipelines/onebranch/variables/common-variables.yml@self - template: /eng/pipelines/onebranch/variables/onebranch-variables.yml@self - - # Define the effective versions for all of the packages we build and release. - - ${{ if parameters.isPreview }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackagePreviewVersion) - - name: effectiveLoggingVersion - value: $(loggingPackagePreviewVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackagePreviewVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackagePreviewVersion) - - name: effectiveAzureVersion - value: $(azurePackagePreviewVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackagePreviewVersion) - - ${{ else }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackageVersion) - - name: effectiveLoggingVersion - value: $(loggingPackageVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackageVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackageVersion) - - name: effectiveAzureVersion - value: $(azurePackageVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackageVersion) + - template: /eng/pipelines/onebranch/variables/package-variables.yml@self + parameters: + isPreview: ${{ parameters.isPreview }} resources: repositories: @@ -150,68 +126,138 @@ resources: extends: # See: https://aka.ms/obpipelines/templates - template: /v2/OneBranch.Official.CrossPlat.yml@templates + template: '/v2/OneBranch.Official.CrossPlat.yml@templates' + parameters: - release: - # This indicates the pipeline category to deploy Box products. See: - # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts - category: NonAzure featureFlags: EnableCDPxPAT: false WindowsHostVersion: 1ESWindows2022 + + release: + # This indicates the pipeline category to deploy Box products. See: + # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts + category: NonAzure + # See: https://aka.ms/obpipelines/sdl globalSdl: - tsa: - # The OneBranch template will set 'break' to false for the other SDL tools when TSA is - # enabled. This allows TSA to gather the results and publish them for downstream analysis. - enabled: true apiscan: + # APIScan "software name" and version parameters are set on a per-job basis with via + # ob_sdl_apiscan_* variables. enabled: true # TODO(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/42858): # We have temporarily disabled breaking the build on ApiScan results until we can: # - Register our new packages with API Scan, and/or # - Publish MSDN/Learn/etc documentation for the new packages. break: false - # Other ApiScan options are set by the jobs via ob_sdl_apiscan_* variables. + + armory: + enabled: true + break: true + + asyncSdl: + # Disabling this as it complicates the build process with minimal gain + enabled: false + + binskim: + enabled: true + break: true + codeql: - compiled: enabled: true - sbom: + compiled: + + credscan: enabled: true - packageName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + suppressionsFile: '$(REPO_ROOT)/.config/CredScanSuppressions.json' + + eslint: + # Only useful for repos with ECMAscript - which we do not have. + enabled: false + policheck: enabled: true break: true - exclusionsFile: $(REPO_ROOT)\.config\PolicheckExclusions.xml - asyncSdl: - enabled: false - credscan: - enabled: true - suppressionsFile: $(REPO_ROOT)/.config/CredScanSuppressions.json - binskim: - enabled: true - armory: - enabled: true - break: true - eslint: - enabled: false - roslyn: - enabled: true - break: true - publishLogs: - enabled: true - tsaOptionsPath: $(REPO_ROOT)\.config\tsaoptions.json - disableLegacyManifest: true + exclusionsFile: '$(REPO_ROOT)\.config\PolicheckExclusions.xml' + + roslyn: + # Note, requires RoslynAnalyzers task to be added as a separate step + enabled: true + break: true + + publishLogs: + enabled: true + + sbom: + enabled: true + packageName: 'Microsoft.Data.SqlClient' + packageVersion: '$(Build.BuildNumber)' + + tsa: + # OneBranch publishes all sdl results to TSA. If TSA is disabled, all SDL tools will be + # forced into 'break' build mode. + enabled: true + configFile: '$(REPO_ROOT)/.config/tsaoptions.json' + stages: - template: /eng/pipelines/onebranch/stages/build-stages.yml@self parameters: - debug: ${{ parameters.debug }} - isPreview: ${{ parameters.isPreview }} + isOfficial: true # This is an official pipeline. + buildSqlServerServer: ${{ parameters.buildSqlServerServer }} + buildSqlClient: ${{ parameters.buildSqlClient }} + buildAkvProvider: ${{ parameters.buildAkvProvider }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsFileVersion: '${{ variables.abstractionsFileVersion }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderFileVersion: '${{ variables.akvProviderFileVersion }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azureFileVersion: '${{ variables.azureFileVersion }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingFileVersion: '${{ variables.loggingFileVersion }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientFileVersion: '${{ variables.sqlClientFileVersion }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerFileVersion: '${{ variables.sqlServerFileVersion }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + + signingAppRegistrationClientId: '$(SigningAppRegistrationClientId)' + signingAppRegistrationTenantId: '$(SigningAppRegistrationTenantId)' + signingAuthAkvName: '$(SigningAuthAkvName)' + signingAuthSignCertName: '$(SigningAuthSignCertName)' + signingEsrpClientId: '$(SigningEsrpClientId)' + signingEsrpConnectedServiceName: '$(SigningEsrpConnectedServiceName)' + + - template: /eng/pipelines/onebranch/stages/publish-symbols-stage.yml@self + parameters: publishSymbols: ${{ parameters.publishSymbols }} buildSqlServerServer: ${{ parameters.buildSqlServerServer }} buildSqlClient: ${{ parameters.buildSqlClient }} - buildAKVProvider: ${{ parameters.buildAKVProvider }} + buildAkvProvider: ${{ parameters.buildAkvProvider }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + + symbolsAzureSubscription: '$(SymbolsAzureSubscription)' + symbolsPublishProjectName: '$(SymbolsPublishProjectNameSqlClient)' + # Official pipelines must publish to the Production symbol server. + symbolsPublishServer: '$(SymbolsPublishServerProd)' + symbolsPublishTokenUri: '$(SymbolsPublishTokenUriProd)' + symbolsUploadAccount: '$(SymbolsUploadAccount)' - template: /eng/pipelines/onebranch/stages/release-stages.yml@self parameters: @@ -220,9 +266,25 @@ extends: # This is an official pipeline. isOfficial: true stageNameSuffix: production + + publishSymbols: ${{ parameters.publishSymbols }} + + abstractionsArtifactsName: '${{ variables.abstractionsArtifactsName }}' + abstractionsPackageVersion: '${{ variables.abstractionsPackageVersion }}' + akvProviderArtifactsName: '${{ variables.akvProviderArtifactsName }}' + akvProviderPackageVersion: '${{ variables.akvProviderPackageVersion }}' + azureArtifactsName: '${{ variables.azureArtifactsName }}' + azurePackageVersion: '${{ variables.azurePackageVersion }}' + loggingArtifactsName: '${{ variables.loggingArtifactsName }}' + loggingPackageVersion: '${{ variables.loggingPackageVersion }}' + sqlClientArtifactsName: '${{ variables.sqlClientArtifactsName }}' + sqlClientPackageVersion: '${{ variables.sqlClientPackageVersion }}' + sqlServerArtifactsName: '${{ variables.sqlServerArtifactsName }}' + sqlServerPackageVersion: '${{ variables.sqlServerPackageVersion }}' + releaseSqlServerServer: ${{ parameters.releaseSqlServerServer }} releaseLogging: ${{ parameters.releaseLogging }} releaseAbstractions: ${{ parameters.releaseAbstractions }} releaseSqlClient: ${{ parameters.releaseSqlClient }} releaseAzure: ${{ parameters.releaseAzure }} - releaseAKVProvider: ${{ parameters.releaseAKVProvider }} + releaseAkvProvider: ${{ parameters.releaseAkvProvider }} diff --git a/eng/pipelines/onebranch/stages/build-stages.yml b/eng/pipelines/onebranch/stages/build-stages.yml index 0b6962f1af..252f63fb0a 100644 --- a/eng/pipelines/onebranch/stages/build-stages.yml +++ b/eng/pipelines/onebranch/stages/build-stages.yml @@ -12,31 +12,14 @@ # - build_independent: builds packages with no cross-package dependencies (Logging, SqlServer) # - build_abstractions: builds the Abstractions package, which depends on Logging # - build_dependent: builds packages with dependencies on Abstractions (SqlClient, Azure) -# - build_addons: builds add-on packages with dependencies on the core packages (AKV Provider) -# -# This template depends on the following runtime (i.e. macro expansion) variables being defined: -# -# - effectiveSqlServerVersion -# - effectiveLoggingVersion -# - effectiveAbstractionsVersion -# - effectiveSqlClientVersion -# - effectiveAzureVersion -# - effectiveAkvProviderVersion +# - build_addons: builds add-on packages with dependencies on the core packages (Akv Provider) parameters: # ── General parameters ───────────────────────────────────────────────── - # True to enable debug information and steps. - - name: debug - type: boolean - - # True if this is a preview build, which uses the preview version numbers from - # common-variables.yml. - - name: isPreview - type: boolean - - # True to publish symbols to public and private servers. - - name: publishSymbols + # True if this is an official build, which runs additional ESRP malware scanning + # and codesigning steps. + - name: isOfficial type: boolean # ── Build parameters ─────────────────────────────────────────────────── @@ -47,9 +30,85 @@ parameters: - name: buildSqlClient type: boolean - - name: buildAKVProvider + - name: buildAkvProvider type: boolean + # Package Parameters ----------------------------------------------------- + + - name: abstractionsArtifactsName + type: string + + - name: abstractionsFileVersion + type: string + + - name: abstractionsPackageVersion + type: string + + - name: akvProviderArtifactsName + type: string + + - name: akvProviderFileVersion + type: string + + - name: akvProviderPackageVersion + type: string + + - name: azureArtifactsName + type: string + + - name: azureFileVersion + type: string + + - name: azurePackageVersion + type: string + + - name: loggingArtifactsName + type: string + + - name: loggingFileVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientArtifactsName + type: string + + - name: sqlClientFileVersion + type: string + + - name: sqlClientPackageVersion + type: string + + - name: sqlServerArtifactsName + type: string + + - name: sqlServerFileVersion + type: string + + - name: sqlServerPackageVersion + type: string + + # Signing Parameters ----------------------------------------------------- + + - name: signingAppRegistrationClientId + type: string + + - name: signingAppRegistrationTenantId + type: string + + - name: signingAuthAkvName + type: string + + - name: signingAuthSignCertName + type: string + + - name: signingEsrpClientId + type: string + + - name: signingEsrpConnectedServiceName + type: string + stages: # ==================================================================== # Stage 1: Independent packages (no cross-package dependencies) @@ -59,41 +118,42 @@ stages: displayName: "Build Independent Packages" jobs: - - ${{ if or(eq(parameters.buildAKVProvider, true), eq(parameters.buildSqlClient, true)) }}: + - ${{ if or(eq(parameters.buildAkvProvider, true), eq(parameters.buildSqlClient, true)) }}: - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self parameters: + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + + isOfficial: ${{ parameters.isOfficial }} packageName: Logging packageFullName: Microsoft.Data.SqlClient.Internal.Logging - packageVersion: $(effectiveLoggingVersion) versionProperties: >- - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - -p:LoggingAssemblyFileVersion=$(loggingAssemblyFileVersion) - assemblyFileVersion: $(loggingAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) + -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} + -p:LoggingAssemblyFileVersion=${{ parameters.loggingFileVersion }} + assemblyFileVersion: '${{ parameters.loggingFileVersion }}' + - ${{ if eq(parameters.buildSqlServerServer, true) }}: - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self parameters: + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + + isOfficial: ${{ parameters.isOfficial }} packageName: SqlServer packageFullName: Microsoft.SqlServer.Server - packageVersion: $(effectiveSqlServerVersion) versionProperties: >- - -p:SqlServerAssemblyFileVersion=$(sqlServerAssemblyFileVersion) - -p:SqlServerPackageVersion=$(effectiveSqlServerVersion) - assemblyFileVersion: $(sqlServerAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) + -p:SqlServerAssemblyFileVersion=${{ parameters.sqlServerFileVersion }} + -p:SqlServerPackageVersion=${{ parameters.sqlServerPackageVersion }} + assemblyFileVersion: '${{ parameters.sqlServerFileVersion }}' # ==================================================================== # Stage 2: Abstractions package (depends on Logging from Stage 1) @@ -101,120 +161,132 @@ stages: # dependency on Internal.Logging. # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: build_abstractions - displayName: "Build Abstractions Package" - dependsOn: build_independent + - stage: build_abstractions + displayName: "Build Abstractions Package" + dependsOn: build_independent - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: Abstractions - packageFullName: Microsoft.Data.SqlClient.Extensions.Abstractions - packageVersion: $(effectiveAbstractionsVersion) - versionProperties: >- - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - -p:AbstractionsAssemblyFileVersion=$(abstractionsAssemblyFileVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - assemblyFileVersion: $(abstractionsAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + + isOfficial: ${{ parameters.isOfficial }} + packageName: Abstractions + packageFullName: Microsoft.Data.SqlClient.Extensions.Abstractions + versionProperties: >- + -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} + -p:AbstractionsAssemblyFileVersion=${{ parameters.abstractionsFileVersion }} + -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} + assemblyFileVersion: '${{ parameters.abstractionsFileVersion }}' + downloadArtifacts: + - artifactName: '${{ parameters.loggingArtifactsName }}' + displayName: 'Logging Package' # ==================================================================== # Stage 3: Core packages (depend on Abstractions) - # MDS and Extensions.Azure build in parallel after Abstractions. + # SqlClient and Extensions.Azure build in parallel after Abstractions. # Stage name kept as 'build_dependent' for validate job compatibility. # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: build_dependent - displayName: "Build Core Packages" - dependsOn: build_abstractions + - stage: build_dependent + displayName: "Build Core Packages" + dependsOn: build_abstractions - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml@self - parameters: - publishSymbols: ${{ parameters.publishSymbols }} - isPreview: ${{ parameters.isPreview }} - # TODO: This job should use the effective versions for Abstractions, Logging, - # SqlServer, and SqlClient. + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml@self + parameters: + isOfficial: ${{ parameters.isOfficial }} + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: Azure - packageFullName: Microsoft.Data.SqlClient.Extensions.Azure - packageVersion: $(effectiveAzureVersion) - versionProperties: >- - -p:AzurePackageVersion=$(effectiveAzureVersion) - -p:AzureAssemblyFileVersion=$(azureAssemblyFileVersion) - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - assemblyFileVersion: $(azureAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(abstractionsArtifactsName) - displayName: Abstractions Package - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + abstractionsArtifactName: '${{ parameters.abstractionsArtifactsName }}' + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingArtifactName: '${{ parameters.loggingArtifactsName }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + sqlClientAssemblyFileVersion: '${{ parameters.sqlClientFileVersion }}' + sqlClientPackageVersion: '${{ parameters.sqlClientPackageVersion }}' + + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + + isOfficial: ${{ parameters.isOfficial }} + packageName: Azure + packageFullName: Microsoft.Data.SqlClient.Extensions.Azure + versionProperties: >- + -p:AzurePackageVersion=${{ parameters.azurePackageVersion }} + -p:AzureAssemblyFileVersion=${{ parameters.azureFileVersion }} + -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} + -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} + assemblyFileVersion: '${{ parameters.azureFileVersion }}' + downloadArtifacts: + - artifactName: '${{ parameters.abstractionsArtifactsName }}' + displayName: Abstractions Package + - artifactName: '${{ parameters.loggingArtifactsName }}' + displayName: Logging Package # ==================================================================== # Stage 4: Add-on packages (depend on core packages) - # AKV Provider builds after MDS completes. + # Akv Provider builds after SqlClient completes. # ==================================================================== - - ${{ if and(eq(parameters.buildAKVProvider, true), eq(parameters.buildSqlClient, true)) }}: - - stage: build_addons - displayName: "Build Add-on Packages" - dependsOn: build_dependent + - ${{ if and(eq(parameters.buildAkvProvider, true), eq(parameters.buildSqlClient, true)) }}: + - stage: build_addons + displayName: "Build Add-on Packages" + dependsOn: build_dependent - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: AkvProvider - packageFullName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider - packageVersion: $(effectiveAkvProviderVersion) - versionProperties: >- - -p:AkvPackageVersion=$(effectiveAkvProviderVersion) - -p:AkvAssemblyFileVersion=$(akvAssemblyFileVersion) - -p:MdsPackageVersion=$(effectiveSqlClientVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - assemblyFileVersion: $(akvAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(sqlClientArtifactsName) - displayName: SqlClient Package - - artifactName: $(abstractionsArtifactsName) - displayName: Abstractions Package - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + signingAppRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + signingAppRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + signingAuthAkvName: '${{ parameters.signingAuthAkvName }}' + signingAuthSignCertName: '${{ parameters.signingAuthSignCertName }}' + signingEsrpClientId: '${{ parameters.signingEsrpClientId }}' + signingEsrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + + isOfficial: ${{ parameters.isOfficial }} + packageName: AkvProvider + packageFullName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + versionProperties: >- + -p:AkvPackageVersion=${{ parameters.akvProviderPackageVersion }} + -p:AkvAssemblyFileVersion=${{ parameters.akvProviderFileVersion }} + -p:MdsPackageVersion=${{ parameters.sqlClientPackageVersion }} + -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} + -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} + assemblyFileVersion: '${{ parameters.akvProviderFileVersion }}' + downloadArtifacts: + - artifactName: '${{ parameters.sqlClientArtifactsName }}' + displayName: SqlClient Package + - artifactName: '${{ parameters.abstractionsArtifactsName }}' + displayName: Abstractions Package + - artifactName: '${{ parameters.loggingArtifactsName }}' + displayName: Logging Package # ==================================================================== # Validation # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: mds_package_validation - displayName: "MDS Package Validation" - dependsOn: build_dependent - jobs: - - template: /eng/pipelines/onebranch/jobs/validate-signed-package-job.yml@self - parameters: - artifactName: $(sqlClientArtifactsName) - isPreview: ${{ parameters.isPreview }} + - stage: sqlclient_package_validation + displayName: "SqlClient Package Validation" + dependsOn: build_dependent + jobs: + - template: /eng/pipelines/onebranch/jobs/validate-signed-package-job.yml@self + parameters: + artifactName: '${{ parameters.sqlClientArtifactsName }}' + expectedFileVersion: '${{ parameters.sqlClientFileVersion }}' + expectedPackageVersion: '${{ parameters.sqlClientPackageVersion }}' + isOfficial: ${{ parameters.isOfficial }} diff --git a/eng/pipelines/onebranch/stages/publish-symbols-stage.yml b/eng/pipelines/onebranch/stages/publish-symbols-stage.yml new file mode 100644 index 0000000000..e716d2255d --- /dev/null +++ b/eng/pipelines/onebranch/stages/publish-symbols-stage.yml @@ -0,0 +1,222 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# Unified symbols publishing stage template for sqlclient OneBranch pipelines. +# Consumed by both the official and non-official pipeline definitions. +# +# This stage publishes PDB symbol files to the internal and public symbol servers. +# Each package's PDBs are published in a separate job to maintain unique naming and +# versioning information per package. +# +# PDBs are expected to be located under the 'symbols/' directory at the root of +# each build artifact, with target framework subdirectories preserved by the build +# job's CopyFiles step. The publish-symbols job consumes that 'symbols' folder directly. +# +# Note that none of the resource DLLs we build produce PDBs, so there are no patterns below that +# match them. +# +# The stage is excluded at compile time when publishSymbols is false. + +parameters: + # ── General parameters ───────────────────────────────────────────────── + + # True to publish symbols (controls whether this stage is emitted at all). + - name: publishSymbols + type: boolean + + # ── Build parameters (used to conditionally include per-package jobs) ─── + + - name: buildSqlServerServer + type: boolean + + - name: buildSqlClient + type: boolean + + - name: buildAkvProvider + type: boolean + + # ── Package Parameters ───────────────────────────────────────────────── + + - name: abstractionsArtifactsName + type: string + + - name: abstractionsPackageVersion + type: string + + - name: akvProviderArtifactsName + type: string + + - name: akvProviderPackageVersion + type: string + + - name: azureArtifactsName + type: string + + - name: azurePackageVersion + type: string + + - name: loggingArtifactsName + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientArtifactsName + type: string + + - name: sqlClientPackageVersion + type: string + + - name: sqlServerArtifactsName + type: string + + - name: sqlServerPackageVersion + type: string + + # ── Symbols Publishing Parameters ────────────────────────────────────── + + - name: symbolsAzureSubscription + type: string + + - name: symbolsPublishProjectName + type: string + + - name: symbolsPublishServer + type: string + + - name: symbolsPublishTokenUri + type: string + + - name: symbolsUploadAccount + type: string + +stages: + # Stage is emitted whenever publishSymbols is true. If no packages were + # built, only the no-op placeholder job runs and the stage succeeds + # immediately — keeping the dependency graph simple for downstream stages. + - ${{ if eq(parameters.publishSymbols, true) }}: + - stage: publish_symbols + displayName: "Publish Symbols" + + # Depend on whichever build stages produced artifacts we need to publish. + dependsOn: + - ${{ if or(parameters.buildSqlServerServer, parameters.buildSqlClient, parameters.buildAkvProvider) }}: + - build_independent + - ${{ if parameters.buildSqlClient }}: + - build_abstractions + - build_dependent + - ${{ if and(parameters.buildAkvProvider, parameters.buildSqlClient) }}: + - build_addons + + jobs: + # ── No-op placeholder ────────────────────────────────────────── + # When no packages were built, this job keeps the stage valid + # (ADO requires at least one job per stage) and succeeds instantly. + - ${{ if not(or(parameters.buildSqlServerServer, parameters.buildSqlClient, parameters.buildAkvProvider)) }}: + - job: publish_symbols_noop + displayName: "No symbols to publish" + pool: + type: linux + steps: + - script: echo "No packages were built — nothing to publish." + displayName: Skip + + # ── Logging ──────────────────────────────────────────────────── + - ${{ if or(parameters.buildAkvProvider, parameters.buildSqlClient) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.loggingArtifactsName }}' + packageName: Logging + packageFullName: Microsoft.Data.SqlClient.Internal.Logging + packageVersion: '${{ parameters.loggingPackageVersion }}' + # Matches: Microsoft.Data.SqlClient.Internal.Logging.pdb across all TFM subdirs. + searchPattern: '**/Microsoft.Data.SqlClient.Internal.Logging.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' + + # ── SqlServer.Server ─────────────────────────────────────────── + - ${{ if eq(parameters.buildSqlServerServer, true) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.sqlServerArtifactsName }}' + packageName: SqlServer + packageFullName: Microsoft.SqlServer.Server + packageVersion: '${{ parameters.sqlServerPackageVersion }}' + # Matches: Microsoft.SqlServer.Server.pdb across all TFM subdirs. + searchPattern: '**/Microsoft.SqlServer.Server.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' + + # ── Abstractions ─────────────────────────────────────────────── + - ${{ if eq(parameters.buildSqlClient, true) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.abstractionsArtifactsName }}' + packageName: Abstractions + packageFullName: Microsoft.Data.SqlClient.Extensions.Abstractions + packageVersion: '${{ parameters.abstractionsPackageVersion }}' + # Matches: Microsoft.Data.SqlClient.Extensions.Abstractions.pdb across all TFM subdirs. + searchPattern: '**/Microsoft.Data.SqlClient.Extensions.Abstractions.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' + + # ── SqlClient ────────────────────────────────────────────────── + - ${{ if eq(parameters.buildSqlClient, true) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.sqlClientArtifactsName }}' + packageName: SqlClient + packageFullName: Microsoft.Data.SqlClient + packageVersion: '${{ parameters.sqlClientPackageVersion }}' + # Matches: Microsoft.Data.SqlClient.pdb across all OS/TFM subdirs. + # Excludes: Microsoft.Data.SqlClient.SNI*.pdb (native SNI — symbols published separately). + searchPattern: '**/Microsoft.Data.SqlClient.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' + + # ── Azure ────────────────────────────────────────────────────── + - ${{ if eq(parameters.buildSqlClient, true) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.azureArtifactsName }}' + packageName: Azure + packageFullName: Microsoft.Data.SqlClient.Extensions.Azure + packageVersion: '${{ parameters.azurePackageVersion }}' + # Matches: Microsoft.Data.SqlClient.Extensions.Azure.pdb across all TFM subdirs. + searchPattern: '**/Microsoft.Data.SqlClient.Extensions.Azure.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' + + # ── AKV Provider ─────────────────────────────────────────────── + - ${{ if and(eq(parameters.buildAkvProvider, true), eq(parameters.buildSqlClient, true)) }}: + - template: /eng/pipelines/onebranch/jobs/publish-symbols-job.yml@self + parameters: + artifactName: '${{ parameters.akvProviderArtifactsName }}' + packageName: AkvProvider + packageFullName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + packageVersion: '${{ parameters.akvProviderPackageVersion }}' + # Matches: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.pdb across all TFM subdirs. + searchPattern: '**/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.pdb' + symbolsAzureSubscription: '${{ parameters.symbolsAzureSubscription }}' + symbolsPublishProjectName: '${{ parameters.symbolsPublishProjectName }}' + symbolsPublishServer: '${{ parameters.symbolsPublishServer }}' + symbolsPublishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + symbolsUploadAccount: '${{ parameters.symbolsUploadAccount }}' diff --git a/eng/pipelines/onebranch/stages/release-stages.yml b/eng/pipelines/onebranch/stages/release-stages.yml index 51eea6e5f0..6ee3fbc88e 100644 --- a/eng/pipelines/onebranch/stages/release-stages.yml +++ b/eng/pipelines/onebranch/stages/release-stages.yml @@ -23,15 +23,6 @@ # - false → NuGet Test feed (via NuGetServiceConnectionTest). # # This template depends on stages defined by the build-stages.yml template. -# -# This template depends on the following runtime (i.e. macro expansion) variables being defined: -# -# - effectiveSqlServerVersion -# - effectiveLoggingVersion -# - effectiveAbstractionsVersion -# - effectiveSqlClientVersion -# - effectiveAzureVersion -# - effectiveAkvProviderVersion parameters: # ── General parameters ───────────────────────────────────────────────── @@ -60,6 +51,52 @@ parameters: - production - test + # Package Parameters ----------------------------------------------------- + + - name: abstractionsArtifactsName + type: string + + - name: abstractionsPackageVersion + type: string + + - name: akvProviderArtifactsName + type: string + + - name: akvProviderPackageVersion + type: string + + - name: azureArtifactsName + type: string + + - name: azurePackageVersion + type: string + + - name: loggingArtifactsName + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientArtifactsName + type: string + + - name: sqlClientPackageVersion + type: string + + - name: sqlServerArtifactsName + type: string + + - name: sqlServerPackageVersion + type: string + + # ── Symbols publishing parameter ─────────────────────────────────────── + # When true, the release stage will depend on publish_symbols so that + # symbol publishing completes before packages are released. + + - name: publishSymbols + type: boolean + default: false + # ── Release parameters ───────────────────────────────────────────────── - name: releaseSqlServerServer @@ -77,7 +114,7 @@ parameters: - name: releaseAzure type: boolean - - name: releaseAKVProvider + - name: releaseAkvProvider type: boolean stages: @@ -94,7 +131,7 @@ stages: # true → NuGet Production feed. # false → NuGet Test feed. # ==================================================================== - - ${{ if or(parameters.releaseSqlServerServer, parameters.releaseLogging, parameters.releaseAbstractions, parameters.releaseSqlClient, parameters.releaseAzure, parameters.releaseAKVProvider) }}: + - ${{ if or(parameters.releaseSqlServerServer, parameters.releaseLogging, parameters.releaseAbstractions, parameters.releaseSqlClient, parameters.releaseAzure, parameters.releaseAkvProvider) }}: - stage: release_${{ parameters.stageNameSuffix }} ${{ if eq(parameters.releaseToProduction, true) }}: displayName: Release to NuGet Production @@ -107,9 +144,11 @@ stages: - build_abstractions - ${{ if or(parameters.releaseSqlClient, parameters.releaseAzure) }}: - build_dependent - - mds_package_validation - - ${{ if parameters.releaseAKVProvider }}: + - sqlclient_package_validation + - ${{ if parameters.releaseAkvProvider }}: - build_addons + - ${{ if parameters.publishSymbols }}: + - publish_symbols variables: - name: onebranchReleaseEnvironment @@ -153,8 +192,8 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.SqlServer.Server - artifactName: drop_build_independent_build_package_SqlServer - packagePath: Microsoft.SqlServer.Server.$(effectiveSqlServerVersion).nupkg + artifactName: '${{ parameters.sqlServerArtifactsName }}' + packagePath: 'packages/Microsoft.SqlServer.Server.${{ parameters.sqlServerPackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} @@ -163,8 +202,8 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Internal.Logging - artifactName: drop_build_independent_build_package_Logging - packagePath: Microsoft.Data.SqlClient.Internal.Logging.$(effectiveLoggingVersion).nupkg + artifactName: '${{ parameters.loggingArtifactsName }}' + packagePath: 'packages/Microsoft.Data.SqlClient.Internal.Logging.${{ parameters.loggingPackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} @@ -173,8 +212,8 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Extensions.Abstractions - artifactName: drop_build_abstractions_build_package_Abstractions - packagePath: Microsoft.Data.SqlClient.Extensions.Abstractions.$(effectiveAbstractionsVersion).nupkg + artifactName: '${{ parameters.abstractionsArtifactsName }}' + packagePath: 'packages/Microsoft.Data.SqlClient.Extensions.Abstractions.${{ parameters.abstractionsPackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} @@ -183,8 +222,8 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient - artifactName: drop_build_dependent_build_package_SqlClient - packagePath: Microsoft.Data.SqlClient.$(effectiveSqlClientVersion).nupkg + artifactName: '${{ parameters.sqlClientArtifactsName }}' + packagePath: 'packages/Microsoft.Data.SqlClient.${{ parameters.sqlClientPackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} @@ -193,18 +232,18 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Extensions.Azure - artifactName: drop_build_dependent_build_package_Azure - packagePath: Microsoft.Data.SqlClient.Extensions.Azure.$(effectiveAzureVersion).nupkg + artifactName: '${{ parameters.azureArtifactsName }}' + packagePath: 'packages/Microsoft.Data.SqlClient.Extensions.Azure.${{ parameters.azurePackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} - - ${{ if eq(parameters.releaseAKVProvider, true) }}: + - ${{ if eq(parameters.releaseAkvProvider, true) }}: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider - artifactName: drop_build_addons_build_package_AkvProvider - packagePath: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.$(effectiveAkvProviderVersion).nupkg + artifactName: '${{ parameters.akvProviderArtifactsName }}' + packagePath: 'packages/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.${{ parameters.akvProviderPackageVersion }}.nupkg' nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} displaySuffix: ${{ variables.nugetTargetSuffix }} diff --git a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml deleted file mode 100644 index b1429e1fd3..0000000000 --- a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml +++ /dev/null @@ -1,58 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# -parameters: - - # The assembly file version to apply to the Abstractions package. - - name: abstractionsAssemblyFileVersion - type: string - - # The version to apply to the Abstractions package. - - name: abstractionsPackageVersion - type: string - - # The assembly file version to apply to the Logging package. - - name: loggingAssemblyFileVersion - type: string - - # The version to apply to the Logging package. - - name: loggingPackageVersion - type: string - - # The assembly file version to apply to the Mds package. - - name: mdsAssemblyFileVersion - type: string - - # The version to apply to the Mds package. - - name: mdsPackageVersion - type: string - -steps: - # Download our signing key. - - task: DownloadSecureFile@1 - displayName: 'Download Key Pair' - inputs: - secureFile: netfxKeypair.snk - name: keyFile - - # Install the .NET SDK. - - template: /eng/pipelines/steps/install-dotnet.yml@self - - - task: MSBuild@1 - displayName: 'BuildAllConfigurations using build.proj' - inputs: - solution: '**/build.proj' - configuration: Release - msbuildArguments: >- - -t:BuildAllConfigurations - -p:ReferenceType=Package - -p:GenerateNuget=false - -p:SigningKeyPath=$(keyFile.secureFilePath) - -p:AssemblyFileVersion=${{ parameters.mdsAssemblyFileVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:LoggingAssemblyFileVersion=${{ parameters.loggingAssemblyFileVersion }} diff --git a/eng/pipelines/onebranch/steps/compound-build-csproj-step.yml b/eng/pipelines/onebranch/steps/build-csproj-step.yml similarity index 69% rename from eng/pipelines/onebranch/steps/compound-build-csproj-step.yml rename to eng/pipelines/onebranch/steps/build-csproj-step.yml index 1f1fee00c4..a4a38b9fc3 100644 --- a/eng/pipelines/onebranch/steps/compound-build-csproj-step.yml +++ b/eng/pipelines/onebranch/steps/build-csproj-step.yml @@ -7,7 +7,7 @@ # Generic build step for csproj-based packages. Each project uses a build.proj target that runs # Build only and produces assemblies within $(BUILD_OUTPUT). Downstream ESRP DLL signing must # locate the assemblies within $(BUILD_OUTPUT) for all target frameworks that the csproj targets. -# NuGet packaging is done separately via compound-pack-csproj-step.yml after DLL signing. +# NuGet packaging is done separately via pack-csproj-step.yml after DLL signing. parameters: # The MSBuild build target in build.proj (e.g. BuildLogging, BuildAbstractions, @@ -15,13 +15,6 @@ parameters: - name: buildTarget type: string - # Build Configuration. - - name: buildConfiguration - type: string - values: - - Debug - - Release - # Additional MSBuild arguments for version properties, e.g. # -p:LoggingPackageVersion=1.0.0 -p:LoggingAssemblyFileVersion=1.0.123 - name: versionProperties @@ -36,15 +29,15 @@ steps: name: keyFile - task: MSBuild@1 - displayName: Build.proj - ${{ parameters.buildTarget }} + displayName: 'Build.proj - ${{ parameters.buildTarget }}' inputs: - solution: $(REPO_ROOT)/build.proj - configuration: ${{ parameters.buildConfiguration }} + solution: '$(REPO_ROOT)/build.proj' + configuration: 'Release' msbuildArguments: >- - -t:${{ parameters.buildTarget }} - -p:ReferenceType=Package - -p:SigningKeyPath=$(keyFile.secureFilePath) - ${{ parameters.versionProperties }} + -t:${{ parameters.buildTarget }} + -p:ReferenceType=Package + -p:SigningKeyPath=$(keyFile.secureFilePath) + ${{ parameters.versionProperties }} - script: tree /a /f $(BUILD_OUTPUT) - displayName: List Build Output Tree + displayName: Output Build Output Tree diff --git a/eng/pipelines/onebranch/steps/build-sqlclient-step.yml b/eng/pipelines/onebranch/steps/build-sqlclient-step.yml new file mode 100644 index 0000000000..390d17fc34 --- /dev/null +++ b/eng/pipelines/onebranch/steps/build-sqlclient-step.yml @@ -0,0 +1,41 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This collection of steps builds Microsoft.Data.SqlClient via build2.proj + +parameters: + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientPackageVersion + type: string + +steps: + - task: DownloadSecureFile@1 + displayName: 'Download Signing Key' + inputs: + secureFile: 'netfxKeypair.snk' + name: keyFile + + - task: MSBuild@1 + displayName: 'Build2.proj - Build SqlClient' + inputs: + solution: '$(REPO_ROOT)/build2.proj' + configuration: 'Release' + msbuildArguments: >- + -t:BuildMds + -p:BuildNumber="$(Build.BuildNumber)" + -p:PackageVersionAbstractions="${{ parameters.abstractionsPackageVersion }}" + -p:PackageVersionLogging="${{ parameters.loggingPackageVersion }}" + -p:PackageVersionMds="${{ parameters.sqlClientPackageVersion }}" + -p:ReferenceType=Package + -p:SigningKeyPath="$(keyFile.secureFilePath)" + + - script: tree /a /f $(BUILD_OUTPUT) + displayName: Output Build Output Tree diff --git a/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml b/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml deleted file mode 100644 index ef1f3b946a..0000000000 --- a/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml +++ /dev/null @@ -1,86 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -parameters: - # The C# build configuration to use (e.g. Debug or Release). - - name: buildConfiguration - type: string - values: - - Debug - - Release - - - name: generateSymbolsPackage - type: boolean - - - name: packageVersion - type: string - - - name: nuspecPath - type: string - - - name: outputDirectory - type: string - - # The C# project reference type to use when building and packing the packages. - - name: referenceType - type: string - values: - # Reference sibling packages as NuGet packages. - - Package - # Reference sibling packages as C# projects. - - Project - - # Semi-colon separated properties to pass to nuget via the -properties argument. - - name: properties - type: string - default: '' - -steps: - # This tool is failing on OneBranch pipelines, possibly due to new - # network isolation rules: - # - # ERR:Client network socket disconnected before secure TLS connection was established - # - # Our AKV Official build uses this 1ES image: - # - # Image: 1ES-OB-2022-D8-Netlock-V2_westus2_1_image - # - # An ICM for this issue exists: - # - # https://portal.microsofticm.com/imp/v5/incidents/details/690355343/summary - # - # Recommendation is to remove this step since NuGet is already present on - # the 1ES images. - # - # - task: NuGetToolInstaller@1 - # displayName: 'Install Latest Nuget' - # inputs: - # checkLatest: true - - - ${{ if parameters.generateSymbolsPackage }}: - - task: NuGetCommand@2 - displayName: 'Generate NuGet Package and Symbols Package' - inputs: - command: custom - arguments: >- - pack - ${{ parameters.nuspecPath }} - -Symbols - -SymbolPackageFormat snupkg - -Version ${{ parameters.packageVersion }} - -OutputDirectory ${{ parameters.outputDirectory }} - -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}" - - ${{ else }}: - - task: NuGetCommand@2 - displayName: 'Generate NuGet Package' - inputs: - command: custom - arguments: >- - pack - ${{ parameters.nuspecPath }} - -Version ${{ parameters.packageVersion }} - -OutputDirectory ${{ parameters.outputDirectory }} - -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}" diff --git a/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml b/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml deleted file mode 100644 index 1b2e3cb3b9..0000000000 --- a/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml +++ /dev/null @@ -1,162 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -# For more details, see https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL - -parameters: - # Name of the symbols artifact that will be published - - name: artifactName - type: string - - # Azure subscription where the publishing task will execute - - name: azureSubscription - type: string - - # Package name, typically the name of the nuget package being built - - name: packageName - type: string - - # Project that symbols will belong to (decided during symbols onboarding) - - name: publishProjectName - type: string - - # Where symbols publishing service is hosted, will be prepended to trafficmanager.net - - name: publishServer - type: string - - # Whether to publish the uploaded symbols to the internal symbols servers - - name: publishToInternal - type: boolean - - # Whether to publish the uploaded symbols to the public symbols servers - - name: publishToPublic - type: boolean - - # URI to use for requesting a bearer-token for publishing the symbols - - name: publishTokenUri - type: string - - # The C# project reference type to use when building and packing the packages. - - name: referenceType - type: string - values: - # Reference sibling packages as NuGet packages. - - Package - # Reference sibling packages as C# projects. - - Project - - # Pattern to use to search for pdb symbols files to upload/publish - - name: searchPattern - type: string - - # Account/org where the symbols will be uploaded - - name: uploadAccount - type: string - - # Version of the symbols to publish, typically the same as the NuGet package version - - name: version - type: string - -steps: - # Set variable for downstream tasks (allegedly). - # - # Note: Because variables cannot be set in top-level of template, this has to be done during - # runtime. - # - - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' - displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' - - - task: PublishSymbols@2 - displayName: 'Upload symbols to ${{ parameters.uploadAccount }} org' - inputs: - IndexSources: false - Pat: '$(System.AccessToken)' - SearchPattern: '${{ parameters.searchPattern }}' - SymbolExpirationInDays: 1825 # 5 years - SymbolServerType: 'TeamServices' - SymbolsArtifactName: '${{ parameters.artifactName }}' - SymbolsFolder: '$(BUILD_OUTPUT)/${{ parameters.referenceType }}/bin' - SymbolsMaximumWaitTime: 60 - SymbolsProduct: '${{ parameters.packageName }}' - SymbolsVersion: '${{ parameters.version }}' - - - task: AzureCLI@2 - displayName: 'Publish Symbols' - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - scriptLocation: inlineScript - scriptType: ps - inlineScript: | - # Propagate parameters to PS variables ################################################ - $artifactName = "${{ parameters.artifactName }}" - echo "artifactName= $artifactName" - - $publishProjectName = "${{ parameters.publishProjectName }}" - echo "publishProjectName= $publishProjectName" - - $publishToInternal = "${{ parameters.publishToInternal }}".ToLower() - echo "publishToInternal= $publishToInternal" - - $publishToPublic = "${{ parameters.publishToPublic }}".ToLower() - echo "publishToPublic= $publishToPublic" - - $publishServer = "${{ parameters.publishServer }}" - echo "publishServer= $publishServer" - - $publishTokenUri = "${{ parameters.publishTokenUri }}" - echo "publishTokenUri= $publishTokenUri" - - # Publish symbols ##################################################################### - # 1) Get the access token for the symbol publishing service - echo "> 1.Acquiring symbol publishing token..." - $symbolPublishingToken = az account get-access-token --resource $publishTokenUri --query accessToken -o tsv - echo "> 1.Symbol publishing token acquired." - - # 2) Register the request name - echo "> 2.Registering request name..." - $requestNameRegistrationBody = "{'requestName': '$artifactName'}" - Invoke-RestMethod ` - -Method POST ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" ` - -Body $requestNameRegistrationBody - echo "> 2.Request name registered successfully." - - # 3) Publish the symbols - echo "> 3.Submitting request to publish symbols..." - $publishSymbolsBody = "{'publishToInternalServer': $publishToInternal, 'publishToPublicServer': $publishToPublic}" - Invoke-RestMethod ` - -Method POST ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" ` - -Body $publishSymbolsBody - echo "> 3.Request to publish symbols submitted successfully." - - # The following REST calls are used to check publishing status. - echo "> 4.Checking the status of the request ..." - Invoke-RestMethod ` - -Method GET ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" - - echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." - - echo "PublishingStatus" - echo "-----------------" - echo "0 NotRequested; The request has not been requested to publish." - echo "1 Submitted; The request is submitted to be published" - echo "2 Processing; The request is still being processed" - echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" - - echo "PublishingResult" - echo "-----------------" - echo "0 Pending; The request has not completed or has not been requested." - echo "1 Succeeded; The request has published successfully" - echo "2 Failed; The request has failed to publish" - echo "3 Cancelled; The request was cancelled" diff --git a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml deleted file mode 100644 index 83053a8d29..0000000000 --- a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml +++ /dev/null @@ -1,166 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# -parameters: - - name: artifactType - values: - - dll - - pkg - - - name: sourceRoot - type: string - default: $(REPO_ROOT) - - - name: dllPattern - type: string - default: 'Microsoft.Data.SqlClient*.dll' - - - name: nupkgPattern - type: string - default: '*.*nupkg' - - - name: artifactDirectory - type: string - default: $(PACK_OUTPUT) - - - name: ESRPConnectedServiceName - type: string - default: $(ESRPConnectedServiceName) - - - name: appRegistrationClientId - type: string - default: $(appRegistrationClientId) - - - name: appRegistrationTenantId - type: string - default: $(appRegistrationTenantId) - - - name: AuthAKVName - type: string - default: $(AuthAKVName) - - - name: AuthSignCertName - type: string - default: $(AuthSignCertName) - - - name: EsrpClientId - type: string - default: $(EsrpClientId) - -steps: -- ${{ if eq(parameters.artifactType, 'dll') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: 'ESRP MalwareScanning' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - FolderPath: '${{parameters.sourceRoot }}' - Pattern: '${{ parameters.dllPattern }}' - CleanupTempStorage: 1 - VerboseLogin: 1 - - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: 'ESRP CodeSigning' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - AuthAKVName: '${{parameters.AuthAKVName }}' - AuthSignCertName: '${{parameters.AuthSignCertName }}' - FolderPath: '${{parameters.sourceRoot }}' - Pattern: '${{ parameters.dllPattern }}' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft Data SqlClient Data Provider for SQL Server" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - -- ${{ if eq(parameters.artifactType, 'pkg') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: 'ESRP MalwareScanning Nuget Package' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '${{ parameters.nupkgPattern }}' - CleanupTempStorage: 1 - VerboseLogin: 1 - - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: 'ESRP CodeSigning Nuget Package' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - AuthAKVName: '${{parameters.AuthAKVName }}' - AuthSignCertName: '${{parameters.AuthSignCertName }}' - FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '${{ parameters.nupkgPattern }}' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] diff --git a/eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml similarity index 76% rename from eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml rename to eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml index 44649f94aa..1398e81766 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml @@ -30,8 +30,9 @@ parameters: - name: esrpClientId type: string - # Globbing pattern for the files to sign. All files in $(BUILD_OUTPUT)/Package/bin - # that match this pattern will be scanned and signed. This should end with ".dll". + # Minimatch pattern(s) for the files to sign. All files in $(BUILD_OUTPUT) + # that match will be scanned and signed. Supports multi-line patterns and + # negation (!pattern). See https://aka.ms/esrp.signtask for details. - name: pattern type: string @@ -43,10 +44,11 @@ steps: AppRegistrationClientId: ${{ parameters.appRegistrationClientId }} AppRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} CleanupTempStorage: 1 - ConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - EsrpClientId: ${{ parameters.esrpClientId }} - FolderPath: $(BUILD_OUTPUT)/Package/bin - Pattern: ${{ parameters.pattern }} + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + FolderPath: '$(BUILD_OUTPUT)' + Pattern: '${{ parameters.pattern }}' + UseMinimatch: true UseMSIAuthentication: true VerboseLogin: 1 @@ -54,14 +56,15 @@ steps: - task: EsrpCodeSigning@6 displayName: ESRP DLL Signing inputs: - AppRegistrationClientId: ${{ parameters.appRegistrationClientId }} - AppRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - AuthAKVName: ${{ parameters.authAkvName }} - AuthSignCertName: ${{ parameters.authSignCertName }} - ConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - EsrpClientId: ${{ parameters.esrpClientId }} - FolderPath: $(BUILD_OUTPUT)/Package/bin - Pattern: ${{ parameters.pattern }} + AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' + AuthAKVName: '${{ parameters.authAkvName }}' + AuthSignCertName: '${{ parameters.authSignCertName }}' + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + FolderPath: '$(BUILD_OUTPUT)' + Pattern: '${{ parameters.pattern }}' + UseMinimatch: true signConfigType: inlineSignParams UseMSIAuthentication: true inlineOperation: | diff --git a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml similarity index 83% rename from eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml rename to eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml index e86964cf3d..6a6682d40f 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml @@ -30,8 +30,13 @@ parameters: - name: esrpClientId type: string - # Glob pattern to match NuGet packages for scanning and signing. - - name: pattern + # Folder path to search for NuGet packages to sign + - name: searchPath + type: string + + # Minimatch pattern(s) to search for NuGet packages. Supports multi-line + # patterns and negation (!pattern). Defaults to '*.*nupkg'. + - name: searchPattern type: string default: '*.*nupkg' @@ -45,8 +50,9 @@ steps: CleanupTempStorage: 1 ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' EsrpClientId: '${{ parameters.esrpClientId }}' - FolderPath: '$(PACK_OUTPUT)' - Pattern: '${{ parameters.pattern }}' + FolderPath: '${{ parameters.searchPath }}' + Pattern: '${{ parameters.searchPattern }}' + UseMinimatch: true UseMSIAuthentication: true VerboseLogin: 1 @@ -60,8 +66,9 @@ steps: EsrpClientId: '${{ parameters.esrpClientId }}' AuthAKVName: '${{ parameters.authAkvName }}' AuthSignCertName: '${{ parameters.authSignCertName }}' - FolderPath: '$(PACK_OUTPUT)' - Pattern: '${{ parameters.pattern }}' + FolderPath: '${{ parameters.searchPath }}' + Pattern: '${{ parameters.searchPattern }}' + UseMinimatch: true signConfigType: 'inlineSignParams' UseMSIAuthentication: true inlineOperation: | diff --git a/eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml b/eng/pipelines/onebranch/steps/pack-csproj-step.yml similarity index 68% rename from eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml rename to eng/pipelines/onebranch/steps/pack-csproj-step.yml index db9b9a8237..535106a9d1 100644 --- a/eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml +++ b/eng/pipelines/onebranch/steps/pack-csproj-step.yml @@ -14,13 +14,6 @@ parameters: - name: packTarget type: string - # Build Configuration. - - name: buildConfiguration - type: string - values: - - Debug - - Release - # Additional MSBuild arguments for version properties. - name: versionProperties type: string @@ -28,15 +21,15 @@ parameters: steps: - task: MSBuild@1 - displayName: Build.proj - ${{ parameters.packTarget }} + displayName: 'Build.proj - ${{ parameters.packTarget }}' inputs: - solution: $(REPO_ROOT)/build.proj - configuration: ${{ parameters.buildConfiguration }} + solution: '$(REPO_ROOT)/build.proj' + configuration: 'Release' msbuildArguments: >- - -t:${{ parameters.packTarget }} - -p:ReferenceType=Package - -p:PackagesDir=$(PACK_OUTPUT)/ - ${{ parameters.versionProperties }} + -t:${{ parameters.packTarget }} + -p:ReferenceType=Package + -p:PackagesDir=$(JOB_OUTPUT)/packages/ + ${{ parameters.versionProperties }} - - script: tree /a /f $(PACK_OUTPUT) + - script: tree /a /f $(JOB_OUTPUT)/packages displayName: List Pack Output Tree After Pack diff --git a/eng/pipelines/onebranch/steps/pack-sqlclient-step.yml b/eng/pipelines/onebranch/steps/pack-sqlclient-step.yml new file mode 100644 index 0000000000..73229fe834 --- /dev/null +++ b/eng/pipelines/onebranch/steps/pack-sqlclient-step.yml @@ -0,0 +1,42 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +parameters: + # Package version parameters ---- + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientPackageVersion + type: string + +steps: + - task: MSBuild@1 + displayName: 'Build2.proj - Pack SqlClient' + inputs: + solution: '$(REPO_ROOT)/build2.proj' + configuration: 'Release' + msbuildArguments: >- + -t:PackMds + -p:BuildNumber="$(Build.BuildNumber)" + -p:PackBuild=false + -p:PackageVersionAbstractions="${{ parameters.abstractionsPackageVersion }}" + -p:PackageVersionLogging="${{ parameters.loggingPackageVersion }}" + -p:PackageVersionMds="${{ parameters.sqlClientPackageVersion }}" + -p:ReferenceType=Package + + - script: tree /a /f $(BUILD_OUTPUT) + displayName: Output Build Output Tree + + - task: CopyFiles@2 + displayName: 'Copy NuGet Packages to JOB_OUTPUT' + inputs: + contents: '**/Microsoft.Data.SqlClient*.*nupkg' + flattenFolders: true + sourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/' + targetFolder: '$(JOB_OUTPUT)/packages' diff --git a/eng/pipelines/onebranch/steps/publish-symbols-step.yml b/eng/pipelines/onebranch/steps/publish-symbols-step.yml index a23dbf4556..d06b3c73e5 100644 --- a/eng/pipelines/onebranch/steps/publish-symbols-step.yml +++ b/eng/pipelines/onebranch/steps/publish-symbols-step.yml @@ -1,119 +1,154 @@ -#################################################################################### -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -# # -# doc: https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL # -#################################################################################### +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# Symbols Publishing Pipeline +# =========================== +# Canonical docs: https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL +# +# Publishing symbols to SymWeb (internal) and MSDL (public) is a two-step process: +# +# Step 1 - Upload: The PublishSymbols@2 task uploads PDB files to the Azure DevOps +# symbol store under an artifact name (SymbolsArtifactName). This stores the +# symbols but does NOT make them available on SymWeb or MSDL. +# +# Step 2 - Publish: The publish-symbols.ps1 script calls the Symbols Publishing Pipeline +# REST API to request that the previously uploaded symbols be published to the +# internal (SymWeb) and/or public (MSDL) symbol servers. +# +# IMPORTANT: Step 2 depends on Step 1. The ArtifactName parameter passed to the publish +# script MUST match the SymbolsArtifactName used by PublishSymbols@2 so that both steps +# reference the same uploaded artifact. + parameters: + # Name of the symbols artifact that will be published + - name: artifactName + type: string + + # Azure subscription where the publishing task will execute + - name: azureSubscription + type: string + + # Package name, typically the name of the nuget package being built + - name: packageName + type: string + + # Project that symbols will belong to (decided during symbols onboarding) + - name: publishProjectName + type: string + + # Where symbols publishing service is hosted, will be prepended to trafficmanager.net + - name: publishServer + type: string + + # Whether to publish the uploaded symbols to the internal symbols servers + - name: publishToInternal + type: boolean + + # Whether to publish the uploaded symbols to the public symbols servers + - name: publishToPublic + type: boolean + + # URI to use for requesting a bearer-token for publishing the symbols + - name: publishTokenUri + type: string + + # Pattern to use to search for pdb symbols files to upload/publish + - name: searchPattern + type: string + + # Root folder to search for PDB files. When called from a build job this is typically + # $(BUILD_OUTPUT); when called from the dedicated symbols stage it points at the + # downloaded artifact path containing the PDBs. + - name: symbolsFolder + type: string + default: '$(BUILD_OUTPUT)' + + # Account/org where the symbols will be uploaded + - name: uploadAccount + type: string - # The full name of the package whose symbols are being published. - - name: packageFullName - type: string - - # The version of the package whose symbols are being published. - - name: packageVersion - type: string - - # Our symbols account name. - - name: symbolsAccount - type: string - default: SqlClientDrivers - - # The symbols server to publish to. - - name: symbolServer - type: string - default: $(SymbolServer) - - # The token URI for the symbol publishing service. - - name: symbolTokenUri - type: string - default: $(SymbolTokenUri) - - # A pair of flags indicating whether to publish to the internal and public symbol servers. Both - # default to true. - - name: publishToServers - type: object - default: - internal: true - public: true + # Version of the symbols to publish, typically the same as the NuGet package version + - name: version + type: string steps: -- pwsh: 'Write-Host "##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{parameters.symbolsAccount}}"' - displayName: 'Set ArtifactServices.Symbol.AccountName to ${{parameters.symbolsAccount}}' - -- pwsh: 'Write-Host "##vso[task.setvariable variable=symbolsArtifactName;]${{ parameters.packageFullName }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.packageVersion }}_$(System.TimelineId)"' - displayName: 'Set symbolsArtifactName variable' - -- task: PublishSymbols@2 - displayName: 'Upload symbols to ${{parameters.symbolsAccount }} org' - inputs: - SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\Package\bin' - SearchPattern: '**/*.pdb' - IndexSources: false - SymbolServerType: TeamServices - SymbolsMaximumWaitTime: 60 - SymbolExpirationInDays: 1825 # 5 years - SymbolsProduct: ${{ parameters.packageFullName }} - SymbolsVersion: ${{ parameters.packageVersion }} - SymbolsArtifactName: $(symbolsArtifactName) - Pat: $(System.AccessToken) - -- task: AzureCLI@2 - displayName: 'Publish symbols' - inputs: - azureSubscription: 'Symbols publishing Workload Identity federation service-ADO.Net' - scriptType: ps - scriptLocation: inlineScript - inlineScript: | - $publishToInternalServer = "${{parameters.publishToServers.internal }}".ToLower() - $publishToPublicServer = "${{parameters.publishToServers.public }}".ToLower() - - echo "Publishing request name: $(symbolsArtifactName)" - echo "Publish to internal server: $publishToInternalServer" - echo "Publish to public server: $publishToPublicServer" - - $symbolServer = "${{parameters.symbolServer }}" - $tokenUri = "${{parameters.symbolTokenUri }}" - # Registered project name in the symbol publishing pipeline: https://portal.microsofticm.com/imp/v3/incidents/incident/520844254/summary - $projectName = "Microsoft.Data.SqlClient.SNI" - - # Get the access token for the symbol publishing service - $symbolPublishingToken = az account get-access-token --resource $tokenUri --query accessToken -o tsv - - echo "> 1.Symbol publishing token acquired." - - echo "Registering the request name ..." - $requestName = "$(symbolsArtifactName)" - $requestNameRegistrationBody = "{'requestName': '$requestName'}" - Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $requestNameRegistrationBody - - echo "> 2.Registration of request name succeeded." - - echo "Publishing the symbols ..." - $publishSymbolsBody = "{'publishToInternalServer': $publishToInternalServer, 'publishToPublicServer': $publishToPublicServer}" - echo "Publishing symbols request body: $publishSymbolsBody" - Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $publishSymbolsBody - - echo "> 3.Request to publish symbols succeeded." - - # The following REST calls are used to check publishing status. - echo "> 4.Checking the status of the request ..." - - Invoke-RestMethod -Method GET -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" - - echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." - - echo "PublishingStatus" - echo "-----------------" - echo "0 NotRequested; The request has not been requested to publish." - echo "1 Submitted; The request is submitted to be published" - echo "2 Processing; The request is still being processed" - echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" - - echo "PublishingResult" - echo "-----------------" - echo "0 Pending; The request has not completed or has not been requested." - echo "1 Succeeded; The request has published successfully" - echo "2 Failed; The request has failed to publish" - echo "3 Cancelled; The request was cancelled" + # Set variable for downstream tasks (allegedly). + # + # Note: Because variables cannot be set in top-level of template, this has to be done during + # runtime. + # + - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' + displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' + + # Log the PDB files that match the search pattern so we can verify no + # unexpected files are included in the upload. + - pwsh: | + $folder = '${{ parameters.symbolsFolder }}' + $glob = '${{ parameters.searchPattern }}' + Write-Host "Symbols folder : $folder" + Write-Host "Search pattern : $glob" + + # Convert the glob to a regex that can match against relative paths. + # ** → match any number of path segments (.*?) + # * → match within a single segment ([^/\\]*) + # ? → match a single non-separator char ([^/\\]) + # . → literal dot + $regex = [regex]::Escape($glob) + $regex = $regex -replace '\\\*\\\*[/\\]?', '.*?' # ** or **/ + $regex = $regex -replace '\\\*', '[^/\\]*' # single * + $regex = $regex -replace '\\\?', '[^/\\]' # single ? + $regex = '^' + $regex + '$' + Write-Host "Regex : $regex" + Write-Host "" + + $allFiles = Get-ChildItem -Path $folder -Recurse -File + $matched = $allFiles | Where-Object { + $rel = $_.FullName.Substring($folder.Length).TrimStart('/\') + $rel -match $regex + } + + if (-not $matched) { + Write-Host "##vso[task.logissue type=warning]No PDB files matched pattern '$glob' under '$folder'" + } else { + $count = @($matched).Count + Write-Host "Matched $count PDB file(s):" + $matched | ForEach-Object { Write-Host " $($_.FullName)" } + } + displayName: 'Log PDBs matching ${{ parameters.searchPattern }}' + + # Step 1 - Upload: Push PDB files to the Azure DevOps symbol store. + # The SymbolsArtifactName set here is the key that links this upload to Step 2. + - task: PublishSymbols@2 + displayName: 'Step 1: Upload symbols to ${{ parameters.uploadAccount }} org' + inputs: + IndexSources: false + Pat: '$(System.AccessToken)' + SearchPattern: '${{ parameters.searchPattern }}' + SymbolExpirationInDays: 1825 # 5 years + SymbolServerType: 'TeamServices' + SymbolsArtifactName: '${{ parameters.artifactName }}' + SymbolsFolder: '${{ parameters.symbolsFolder }}' + SymbolsMaximumWaitTime: 60 + SymbolsProduct: '${{ parameters.packageName }}' + SymbolsVersion: '${{ parameters.version }}' + + # Step 2 - Publish: Request the Symbols Publishing Pipeline to publish the uploaded + # symbols to SymWeb and/or MSDL. The -ArtifactName argument must match the + # SymbolsArtifactName from Step 1. + - task: AzureCLI@2 + displayName: 'Step 2: Publish symbols to SymWeb/MSDL' + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + scriptType: pscore + scriptLocation: scriptPath + scriptPath: '$(Build.SourcesDirectory)/eng/pipelines/onebranch/scripts/publish-symbols.ps1' + arguments: >- + -PublishServer "${{ parameters.publishServer }}" + -PublishTokenUri "${{ parameters.publishTokenUri }}" + -PublishProjectName "${{ parameters.publishProjectName }}" + -ArtifactName "${{ parameters.artifactName }}" + -PublishToInternal $${{ parameters.publishToInternal }} + -PublishToPublic $${{ parameters.publishToPublic }} diff --git a/eng/pipelines/onebranch/steps/code-analyze-step.yml b/eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml similarity index 68% rename from eng/pipelines/onebranch/steps/code-analyze-step.yml rename to eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml index c112493603..53b17d4bb9 100644 --- a/eng/pipelines/onebranch/steps/code-analyze-step.yml +++ b/eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml @@ -5,42 +5,35 @@ ################################################################################# # This template defines a step to run Roslyn Analyzers against builds driven by -# build.proj. It is used for both the full Microsoft.Data.SqlClient (MDS) build +# build.proj. It is used for both the full Microsoft.Data.SqlClient (SqlClient) build # and for individual extension packages; callers control the analyzed targets # via the msBuildArguments parameter. # It uses the RoslynAnalyzers@3 task from the Secure Development Team's SDL # extension: -# # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task # -# GOTCHA: This step will clobber any existing build output. It should be run +# GOTCHA: This step will clobber any existing build output. It should be run # _before_ any build steps that perform versioning or signing. parameters: - # Source Root. - - name: sourceRoot + # Target to build from build.proj. This is separated from the rest of the msBuildArguments to + # provide a rich display name. + - name: buildTarget type: string - default: $(REPO_ROOT) - # MSBuild arguments appended after the project file path. The default targets - # BuildAllConfigurations for the full MDS build; override this when analyzing - # individual extension packages. + # Additional build arguments to append to the end of the MSBuild command. - name: msBuildArguments type: string - default: >- - -t:BuildAllConfigurations - -p:configuration=Release - -p:GenerateNuget=false - -p:BuildTools=false steps: - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 - displayName: Roslyn Analyzers + displayName: 'Roslyn Analyzers: build.proj ${{ parameters.buildTarget }}' inputs: msBuildVersion: 17.0 msBuildArchitecture: x64 setupCommandLinePicker: vs2022 msBuildCommandLine: >- msbuild - ${{parameters.sourceRoot}}\build.proj - ${{parameters.msBuildArguments}} + $(REPO_ROOT)\build.proj + -t:${{ parameters.buildTarget }} + ${{ parameters.msBuildArguments }} diff --git a/eng/pipelines/onebranch/steps/roslyn-analyzers-sqlclient-step.yml b/eng/pipelines/onebranch/steps/roslyn-analyzers-sqlclient-step.yml new file mode 100644 index 0000000000..ac515aa021 --- /dev/null +++ b/eng/pipelines/onebranch/steps/roslyn-analyzers-sqlclient-step.yml @@ -0,0 +1,40 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This template defines a step to run Roslyn Analyzers on the SqlClient build. It uses the +# RoslynAnalyzers@3 task from the Secure Development Team's SDL extension: +# +# https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task +# +# GOTCHA: This step will clobber any existing build output. It should be run _before_ any build +# steps that perform versioning or signing. + +parameters: + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: sqlClientPackageVersion + type: string + +steps: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 + displayName: 'Roslyn Analyzers: build2.proj Build SqlClient' + inputs: + msBuildArchitecture: x64 + msBuildCommandLine: >- + msbuild + $(REPO_ROOT)/build2.proj + -t:BuildMds + -p:Configuration=Release + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.sqlClientPackageVersion }} + -p:ReferenceType=Package + msBuildVersion: 17.0 + setupCommandLinePicker: vs2022 diff --git a/eng/pipelines/onebranch/variables/common-variables.yml b/eng/pipelines/onebranch/variables/common-variables.yml index 35236f7ff6..3df1a6264f 100644 --- a/eng/pipelines/onebranch/variables/common-variables.yml +++ b/eng/pipelines/onebranch/variables/common-variables.yml @@ -7,181 +7,37 @@ # Common variables shared across all OneBranch Official pipelines. variables: - - group: Release Variables - - # Symbols publishing credentials - - group: Symbols publishing - # SymbolsAzureSubscription - # SymbolsPublishProjectName - # SymbolsPublishServer - # SymbolsPublishTokenUri - # SymbolsUploadAccount - - - group: ESRP Federated Creds (AME) - # ESRPConnectedServiceName - # ESRPClientId - # AppRegistrationClientId - # AppRegistrationTenantId - # AuthAKVName - # AuthSignCertName - - name: CommitHead value: '' # the value will be extracted from the repo's head - # Aliases required by compound step templates (compound-esrp-dll-signing-step, - # compound-esrp-nuget-signing-step, compound-nuget-pack-step, etc.). + ########################################################################## + # "Well-Known" Variables that are ok to use directly, anywhere in the pipeline. # The root of our repo. - name: REPO_ROOT value: $(Build.SourcesDirectory) # This is where our C# projects place their build outputs (see Directory.Build.props - # ). We exclude the reference type here since Official pipelines always use + # ). We exclude the reference type here since Official pipelines always use # Package mode. - name: BUILD_OUTPUT value: $(REPO_ROOT)/artifacts - # This is where our C# projects place their NuGet package outputs. This is intentionally - # separate from packages/ (where downloaded pipeline artifacts go) so that ESRP signing and - # OneBranch artifact publishing only operate on packages built by the current job. - - name: PACK_OUTPUT + # Directory where downloaded pipeline artifacts (NuGet packages from earlier + # stages) are placed. Build jobs use this as a local NuGet package source so + # that downstream packages can resolve dependencies on packages built by + # upstream stages. + - name: JOB_INPUT + value: $(REPO_ROOT)/packages + + # Root directory for all job output artifacts (NuGet packages, symbols, etc.). + # OneBranch auto-publishes everything under this directory. It is intentionally + # separated from packages/ (where downloaded pipeline artifacts go) so that ESRP + # signing and OneBranch artifact publishing only operate on the current job's output. + # + # Sub-directory layout: + # assemblies/ - DLL assemblies for APIScan (preserving TFM folder structure) + # packages/ - NuGet packages (.nupkg, .snupkg) + # symbols/ - PDB symbol files (preserving TFM folder structure) + - name: JOB_OUTPUT value: $(REPO_ROOT)/output - - # C# assembly versions must be in the format: Major.Minor.Build.Revision, but - # $(Build.BuildNumber) has the format XXX.YY. Additionally, each version part - # must be a positive 16-bit integer less than 65535. Simply concatenating - # both parts of $(Build.BuildNumber) could produce values larger than 65534, - # so we must omit the second part entirely. Unfortunately, this may result - # in multiple subsequent pipline builds using the same C# assembly versions. - # The package versions will not be affected and will show the complete - # $(Build.BuildNumber) values. - - name: assemblyBuildNumber - value: $[ split(variables['Build.BuildNumber'], '.')[0] ] - - # ---------------------------------------------------------------------------- - # Abstractions Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: abstractionsPackageVersion - value: '1.0.0' - - # The NuGet package version for preview releases. - - name: abstractionsPackagePreviewVersion - value: 1.0.0-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the Abstractions package. - - name: abstractionsAssemblyFileVersion - value: 1.0.0.$(assemblyBuildNumber) - - # ---------------------------------------------------------------------------- - # MDS Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: mdsPackageVersion - value: '7.0.1' - - # The NuGet package version for preview releases. - # - # Since we're on the release/7.0 branch, there will never be another legitimate preview release. - # However, we routinely want to run preview builds to test other changes, so we continue to - # maintain this variable such that these testing preview builds receive newer version numbers - # than previous builds. - - name: mdsPackagePreviewVersion - value: 7.0.2-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the MDS package. - - name: mdsAssemblyFileVersion - value: 7.0.1.$(assemblyBuildNumber) - - # The path to the NuGet packaging specification file used to generate the MDS NuGet package. - - name: nuspecPath - value: '$(REPO_ROOT)/tools/specs/Microsoft.Data.SqlClient.nuspec' - - # ---------------------------------------------------------------------------- - # Logging Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: loggingPackageVersion - value: '1.0.0' - - # The NuGet package version for preview releases. - - name: loggingPackagePreviewVersion - value: 1.0.0-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the Logging package. - - name: loggingAssemblyFileVersion - value: 1.0.0.$(assemblyBuildNumber) - - # ---------------------------------------------------------------------------- - # Azure Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: azurePackageVersion - value: '1.0.0' - - # The NuGet package version for preview releases. - - name: azurePackagePreviewVersion - value: 1.0.0-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the Azure package. - - name: azureAssemblyFileVersion - value: 1.0.0.$(assemblyBuildNumber) - - # ---------------------------------------------------------------------------- - # SqlServer.Server Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: sqlServerPackageVersion - value: '1.0.0' - - # The NuGet package version for preview releases. - - name: sqlServerPackagePreviewVersion - value: 1.0.0-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the SqlServer.Server package. - - name: sqlServerAssemblyFileVersion - value: 1.0.0.$(assemblyBuildNumber) - - # ---------------------------------------------------------------------------- - # AKV Provider Package Versions - # - # These are version values that will be used by the official build. They - # should be updated after each release to reflect the next release's versions. - - # The NuGet package version for GA releases (non-preview). - - name: akvPackageVersion - value: '7.0.0' - - # The NuGet package version for preview releases. - - name: akvPackagePreviewVersion - value: 7.0.0-preview1.$(Build.BuildNumber) - - # The AssemblyFileVersion for all assemblies in the AKV Provider package. - - name: akvAssemblyFileVersion - value: 7.0.0.$(assemblyBuildNumber) - - # ---------------------------------------------------------------------------- - # MDS Symbols Publishing - # - # Alias variables for MDS publish-symbols-step.yml, which defaults to - # $(SymbolServer) and $(SymbolTokenUri). These map to the values provided - # by the "Symbols publishing" library group. - - name: SymbolServer - value: $(SymbolsPublishServer) - - name: SymbolTokenUri - value: $(SymbolsPublishTokenUri) diff --git a/eng/pipelines/onebranch/variables/onebranch-variables.yml b/eng/pipelines/onebranch/variables/onebranch-variables.yml index 9c60ff4cf0..b3bebb0a64 100644 --- a/eng/pipelines/onebranch/variables/onebranch-variables.yml +++ b/eng/pipelines/onebranch/variables/onebranch-variables.yml @@ -4,52 +4,41 @@ # See the LICENSE file in the project root for more information. # ################################################################################# -# This file is only included in MDS OneBranch Official pipelines. +# This file is only included in SqlClient OneBranch official/non-official pipelines. variables: - - template: /eng/pipelines/onebranch/variables/common-variables.yml@self + # Libraries ============================================================== - # onebranch template variables - - name: ob_sdl_binskim_break - value: true # https://aka.ms/obpipelines/sdl - - name: Packaging.EnableSBOMSigning - value: true - - name: WindowsContainerImage - value: "onebranch.azurecr.io/windows/ltsc2022/vse2022:latest" # Docker image which is used to build the project https://aka.ms/obpipelines/containers - - # OneBranch automatically publishes pipeline artifacts from all jobs. Use of the - # PublishPipelineArtifacts task is prohibited. All content in the ob_outputDirectory is - # published. The artifacts are named according to this pattern: - # - # drop__ + # These variables are used for running ESRP signing tasks. All names start with "Signing" to make + # it clear that these variables are used for signing (as opposed to other msc tasks). # - # Downstream stages and jobs may download these artifacts via the DownloadPipelineArtifact task, - # but they must know the artifact name. + # SigningAppRegistrationClientId + # SigningAppRegistrationTenantId + # SigningAuthAkvName + # SigningAuthSignCertName + # SigningEsrpClientId + # SigningEsrpConnectedServiceName + - group: 'esrp-variables-v2' + + # These variables are used for running symbol publishing tasks. All names start with "Symbols" to + # make it clear that these variables are used for symbols (as opposed to other msc tasks). # - # Here we define the artifact names that our OneBranch pipelines produce to avoid hardcoding them - # throughout the pipeline files. If any of the stage or job names change, we only need to update - # the artifact names here. - - # The Abstractions package artifacts. - - name: abstractionsArtifactsName - value: drop_build_abstractions_build_package_Abstractions + # SymbolsAzureSubscription + # SymbolsPublishProjectNameSqlClient + # SymbolsPublishServerProd / SymbolsPublishServerPPE + # SymbolsPublishTokenUriProd / SymbolsPublishTokenUriPPE + # SymbolsUploadAccount + - group: 'Symbols Publishing' - # The AKV Provider package artifacts. - - name: akvArtifactsName - value: drop_build_addons_build_package_AkvProvider + # OneBranch Template Variables ########################################### - # The Azure package artifacts. - - name: azureArtifactsName - value: drop_build_dependent_build_package_Azure - - # The Logging package artifacts. - - name: loggingArtifactsName - value: drop_build_independent_build_package_Logging + # https://aka.ms/obpipelines/sdl + - name: ob_sdl_binskim_break + value: true - # The SqlServer package artifacts. - - name: sqlServerArtifactsName - value: drop_build_independent_build_package_SqlServer + - name: Packaging.EnableSBOMSigning + value: true - # The SqlClient package artifacts. - - name: sqlClientArtifactsName - value: drop_build_dependent_build_package_SqlClient + # Docker image which is used to build the project https://aka.ms/obpipelines/containers + - name: WindowsContainerImage + value: "onebranch.azurecr.io/windows/ltsc2022/vse2022:latest" diff --git a/eng/pipelines/onebranch/variables/package-variables.yml b/eng/pipelines/onebranch/variables/package-variables.yml new file mode 100644 index 0000000000..a07193bbe5 --- /dev/null +++ b/eng/pipelines/onebranch/variables/package-variables.yml @@ -0,0 +1,149 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This file contains variables that relate to the various packages that are produced via the +# OneBranch official/non-official pipelines. They are grouped by the packages they represent. +# +# The variables are defined as the following main types: +# - *ArtifactsName +# What the artifacts drop for the various stages of the pipeline will be named. +# +# OneBranch automatically publishes pipeline artifacts from all jobs. Use of the +# PublishPipelineArtifacts task is prohibited. All content in the ob_outputDirectory is +# automatically published. The artifacts are named according to this pattern: +# +# drop__ +# +# Downstream stages and jobs may download these artifacts via the DownloadPipelineArtifact task, +# but they must know the artifact name. +# +# - *FileVersion +# Version to use for the FileVersion field in the assembly. +# +# - *PackageVersion +# NuGet package versions releases. This is switched based on the isPreview parameter. + +# @TODO: Although defining the versions in source is a major improvement over depending on versions +# coming from variable libraries, this is not the final form. Ideally, we want a single point for +# versions to be defined and are moving towards doing so via the [PackageName]Versions.props +# files. In the meantime, when doing a version bump (after a release), versions will need to be +# updated here and in their corresponding *Versions.props file. + +parameters: + - name: isPreview + type: boolean + +variables: + # C# assembly versions must be in the format: Major.Minor.Build.Revision, but + # $(Build.BuildNumber) has the format XXX.YY. Additionally, each version part must be a positive + # 16-bit integer less than 65535. Simply concatenating both parts of $(Build.BuildNumber) could + # produce values larger than 65534, so we must omit the second part entirely. Unfortunately, this + # may result in multiple subsequent pipeline builds using the same C# assembly versions. The + # package versions will not be affected and will show the complete $(Build.BuildNumber) values. + - name: _assemblyBuildNumber + value: $[ split(variables['Build.BuildNumber'], '.')[0] ] + + # ------------------------------------------------------------------------ + # Abstractions Package + # Microsoft.Data.SqlClient.Extensions.Abstractions + - name: abstractionsArtifactsName + value: 'drop_build_abstractions_build_package_Abstractions' + + - ${{ if parameters.isPreview }}: + - name: abstractionsFileVersion + value: '1.0.2.$(_assemblyBuildNumber)' + - name: abstractionsPackageVersion + value: '1.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: abstractionsFileVersion + value: '1.0.1.$(_assemblyBuildNumber)' + - name: abstractionsPackageVersion + value: '1.0.1' + + # ------------------------------------------------------------------------ + # AKV Provider Package + # Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVault + - name: akvProviderArtifactsName + value: 'drop_build_addons_build_package_AkvProvider' + + - ${{ if parameters.isPreview }}: + - name: akvProviderFileVersion + value: '7.0.2.$(_assemblyBuildNumber)' + - name: akvProviderPackageVersion + value: '7.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: akvProviderFileVersion + value: '7.0.1.$(_assemblyBuildNumber)' + - name: akvProviderPackageVersion + value: '7.0.1' + + # ------------------------------------------------------------------------ + # Azure Package + # Microsoft.Data.SqlClient.Extensions.Azure + - name: azureArtifactsName + value: 'drop_build_dependent_build_package_Azure' + + - ${{ if parameters.isPreview }}: + - name: azureFileVersion + value: '1.0.2.$(_assemblyBuildNumber)' + - name: azurePackageVersion + value: '1.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: azureFileVersion + value: '1.0.1.$(_assemblyBuildNumber)' + - name: azurePackageVersion + value: '1.0.1' + + # ------------------------------------------------------------------------ + # Logging Package + # Microsoft.Data.SqlClient.Internal.Logging + - name: loggingArtifactsName + value: 'drop_build_independent_build_package_Logging' + + - ${{ if parameters.isPreview }}: + - name: loggingFileVersion + value: '1.0.2.$(_assemblyBuildNumber)' + - name: loggingPackageVersion + value: '1.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: loggingFileVersion + value: '1.0.1.$(_assemblyBuildNumber)' + - name: loggingPackageVersion + value: '1.0.1' + + # ------------------------------------------------------------------------ + # SqlClient Package + # Microsoft.Data.SqlClient + - name: sqlClientArtifactsName + value: 'drop_build_dependent_build_package_SqlClient' + + - ${{ if parameters.isPreview }}: + - name: sqlClientFileVersion + value: '7.0.2.$(_assemblyBuildNumber)' + - name: sqlClientPackageVersion + value: '7.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: sqlClientFileVersion + value: '7.0.1.$(_assemblyBuildNumber)' + - name: sqlClientPackageVersion + value: '7.0.1' + + # ------------------------------------------------------------------------ + # SqlServer Package + # Microsoft.SqlServer.Server + - name: sqlServerArtifactsName + value: 'drop_build_independent_build_package_SqlServer' + + - ${{ if parameters.isPreview }}: + - name: sqlServerFileVersion + value: '1.0.2.$(_assemblyBuildNumber)' + - name: sqlServerPackageVersion + value: '1.0.2-preview1.$(Build.BuildNumber)' + - ${{ else }}: + - name: sqlServerFileVersion + value: '1.0.1.$(_assemblyBuildNumber)' + - name: sqlServerPackageVersion + value: '1.0.1' diff --git a/eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml b/eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml deleted file mode 100644 index 75ad369dbc..0000000000 --- a/eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml +++ /dev/null @@ -1,38 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -# This file is only included in MDS OneBranch Official pipelines. - -variables: - - group: Release Variables - - template: /eng/pipelines/onebranch/variables/common-variables.yml@self - - - name: TempFolderName # extract the nuget package here - value: temp - - name: extractedNugetRootPath - value: $(Build.SourcesDirectory)\$(TempFolderName)\Microsoft.Data.SqlClient - - name: extractedNugetPath - value: $(extractedNugetRootPath).$(mdsPackageVersion) - - name: expectedFolderNames - value: lib,ref,runtimes - - name: expectedDotnetVersions - value: netstandard2.0,net462,net8.0,net9.0 - - name: Database - value: Northwind - - name: platform - value: AnyCPU - - name: TargetNetFxVersion - value: net481 - - name: TargetNetCoreVersion - value: net9.0 - - name: SQLTarget - value: localhost - - name: encrypt - value: false - - name: SQL_NP_CONN_STRING - value: Data Source=np:$(SQLTarget);Initial Catalog=$(Database);Integrated Security=true;Encrypt=$(ENCRYPT);TrustServerCertificate=true; - - name: SQL_TCP_CONN_STRING - value: Data Source=tcp:$(SQLTarget);Initial Catalog=$(Database);Integrated Security=true;Encrypt=$(ENCRYPT);TrustServerCertificate=true;