From 5388c36b358a5ba5d53c097184183b215bd0a54f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 13 Aug 2026 07:19:24 -0400 Subject: [PATCH 1/2] ci: refreshes ci configuration with recent updates from main --- .azure-pipelines/ci-build.yml | 31 ++++- .../1espt/PipelineAutobaseliningConfig.yml | 15 ++- .config/guardian/.gdnbaselines | 53 ++++++++ .github/workflows/auto-merge-dependabot.yml | 2 +- .github/workflows/ci-cd.yml | 16 ++- .github/workflows/codeql-analysis.yml | 15 ++- .github/workflows/promote-shipped-apis.yml | 113 ++++++++++++++++++ .github/workflows/release-please-gha.yml | 41 ++++++- .github/workflows/sonarcloud.yml | 14 ++- Dockerfile | 4 +- scripts/promoteUnshipped.ps1 | 13 ++ 11 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 .config/guardian/.gdnbaselines create mode 100644 .github/workflows/promote-shipped-apis.yml create mode 100644 scripts/promoteUnshipped.ps1 diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index ee70c8319..f705dfdb1 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -65,6 +65,11 @@ extends: displayName: 'Use .NET 8' inputs: version: 8.x + + - task: UseDotNet@2 + displayName: 'Use .NET 10' + inputs: + useGlobalJson: true # Install the nuget tool. - task: NuGetToolInstaller@1 @@ -74,6 +79,20 @@ extends: checkLatest: true # Build the Product project + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to Azure Artifacts' + + - pwsh: | + @" + + + + + + + + "@ | Set-Content -Path "$(Build.SourcesDirectory)/nuget.config" -Encoding UTF8 + displayName: 'Create nuget.config (central feed)' - task: DotNetCoreCLI@2 displayName: 'build' inputs: @@ -88,7 +107,7 @@ extends: projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.sln' arguments: '--configuration $(BuildConfiguration) --no-build' - - task: EsrpCodeSigning@5 + - task: EsrpCodeSigning@6 displayName: 'ESRP CodeSigning binaries' inputs: ConnectedServiceName: 'Federated DevX ESRP Managed Identity Connection' @@ -154,7 +173,7 @@ extends: - pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg displayName: 'pack Hidi' - - task: EsrpCodeSigning@5 + - task: EsrpCodeSigning@6 displayName: 'ESRP CodeSigning Nuget Packages' inputs: ConnectedServiceName: 'Federated DevX ESRP Managed Identity Connection' @@ -193,7 +212,7 @@ extends: displayName: publish Hidi as executable inputs: command: 'publish' - arguments: -c Release --runtime win-x64 /p:PublishSingleFile=true /p:PackAsTool=false --self-contained --output $(Build.ArtifactStagingDirectory)/Microsoft.OpenApi.Hidi + arguments: -c Release --runtime win-x64 -p:RestoreConfigFile=$(Build.SourcesDirectory)\nuget.config /p:PublishSingleFile=true /p:PackAsTool=false --self-contained --output $(Build.ArtifactStagingDirectory)/Microsoft.OpenApi.Hidi projects: 'src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj' publishWebProjects: False zipAfterPublish: false @@ -472,4 +491,8 @@ extends: -t "$(REGISTRY)/$(IMAGE_NAME):${VERSION}" \ "$(Pipeline.Workspace)" displayName: 'Build and Push Release Image' - condition: contains(variables['Build.SourceBranch'], 'refs/tags/v') \ No newline at end of file + condition: contains(variables['Build.SourceBranch'], 'refs/tags/v') + +# once the nuget has been released, fill this form to get the public documentation updated. +# https://dev.azure.com/msft-skilling/Content/_workitems/create/User%20Story?templateId=39fb91e3-64a2-4c8a-83db-b2bdf3603dd3&ownerId=c4a28f90-17ae-4384-b514-7273392b082b +# https://learn.microsoft.com/en-us/dotnet/api/microsoft.openapi diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml index 2425160a4..37a1a42bd 100644 --- a/.config/1espt/PipelineAutobaseliningConfig.yml +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -8,8 +8,17 @@ pipelines: credscan: lastModifiedDate: 2024-09-13 eslint: - lastModifiedDate: 2024-09-13 + lastModifiedDate: 2026-05-13 psscriptanalyzer: - lastModifiedDate: 2024-09-13 + lastModifiedDate: 2026-05-13 armory: - lastModifiedDate: 2024-09-13 + lastModifiedDate: 2026-05-13 + accessibilityinsights: + lastModifiedDate: 2025-06-03 + binary: + credscan: + lastModifiedDate: 2025-01-28 + binskim: + lastModifiedDate: 2026-05-13 + spotbugs: + lastModifiedDate: 2026-05-13 diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines new file mode 100644 index 000000000..5ed019a92 --- /dev/null +++ b/.config/guardian/.gdnbaselines @@ -0,0 +1,53 @@ +{ + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" + }, + "version": "1.0.0", + "baselines": { + "default": { + "name": "default", + "createdDate": "2026-05-13 00:39:09Z", + "lastUpdatedDate": "2026-05-13 00:40:41Z" + } + }, + "results": { + "89411c6a070fd31f2836b38a16926ffbfb4882aff49c907cafe6c95dfba1104a": { + "signature": "89411c6a070fd31f2836b38a16926ffbfb4882aff49c907cafe6c95dfba1104a", + "alternativeSignatures": [ + "59104f1c3fec111fba3f6b5b6d2d37f3bc0d5267b425f82b8c3f852abc8b2b7a", + "4dee2b2a1887d7a952ae5c61e48c9b6f543096940991cdcd0209c60561a537ce", + "558e542a1dcc76100e4395b278f8b99f1dab960b2b3ae01a4c5cb84ac5cdc70f" + ], + "target": "src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs", + "line": 152, + "uriBaseId": "file:///D:/a/_work/1/s/", + "memberOf": [ + "default" + ], + "tool": "roslynanalyzers", + "ruleId": "CA1310", + "createdDate": "2026-05-13 00:39:09Z", + "expirationDate": "2026-10-30 00:42:11Z", + "justification": "This error is baselined with an expiration date of 180 days from 2026-05-13 00:42:11Z" + }, + "f6386b2f2aa1886a31fd1b57fa9e476ff5f24efbe144c82246f931e688010861": { + "signature": "f6386b2f2aa1886a31fd1b57fa9e476ff5f24efbe144c82246f931e688010861", + "alternativeSignatures": [ + "eb5bc1ea05b68f4106e441c8d441b39afaea833e5b2cbf6804d34cb869d9ed00", + "bbe095dbf06a503925246de4a84cdc73afdafeddde3dab2440302c40560f811e", + "aaac06605663a64f92dc03d6aacf9a424f2d861eba86f4ffa06cd231479734a8" + ], + "target": "src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs", + "line": 179, + "uriBaseId": "file:///D:/a/_work/1/s/", + "memberOf": [ + "default" + ], + "tool": "roslynanalyzers", + "ruleId": "CA1310", + "createdDate": "2026-05-13 00:39:09Z", + "expirationDate": "2026-10-30 00:42:11Z", + "justification": "This error is baselined with an expiration date of 180 days from 2026-05-13 00:42:11Z" + } + } +} \ No newline at end of file diff --git a/.github/workflows/auto-merge-dependabot.yml b/.github/workflows/auto-merge-dependabot.yml index d454cd186..a3343774e 100644 --- a/.github/workflows/auto-merge-dependabot.yml +++ b/.github/workflows/auto-merge-dependabot.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v2.4.0 + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 9d55602b3..9c903a633 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -9,19 +9,29 @@ jobs: ci: name: Continuous Integration runs-on: ubuntu-latest + permissions: + contents: read + code-quality: write + pull-requests: read env: ARTIFACTS_FOLDER: ${{ github.workspace }}/Artifacts GITHUB_RUN_NUMBER: ${{ github.run_number }} steps: - - name: Setup .NET - uses: actions/setup-dotnet@v5 + - name: Setup .NET 8 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 8.x + - name: Setup .NET 10 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: 10.x + - name: Checkout repository id: checkout_repo - uses: actions/checkout@v5 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3135b775a..e71d2f236 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,16 +20,21 @@ jobs: steps: - name: Checkout repository id: checkout_repo - uses: actions/checkout@v5 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Setup .NET - uses: actions/setup-dotnet@v5 + - name: Setup .NET 8 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 8.0.x + - name: Setup .NET 10 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: 10.0.x + - name: Initialize CodeQL id: init_codeql - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: queries: security-and-quality @@ -49,6 +54,6 @@ jobs: - name: Perform CodeQL Analysis id: analyze_codeql - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 # Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/.github/workflows/promote-shipped-apis.yml b/.github/workflows/promote-shipped-apis.yml new file mode 100644 index 000000000..ea0cd692e --- /dev/null +++ b/.github/workflows/promote-shipped-apis.yml @@ -0,0 +1,113 @@ +name: Promote Shipped APIs + +on: + push: + branches: + - main + - support/v2 + workflow_dispatch: + +jobs: + promote-apis: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }} + + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Configure git + shell: pwsh + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global url."https://$($env:GH_TOKEN)@github.com/".insteadOf "https://github.com/" + + - name: Check for existing PR + id: check_pr + shell: pwsh + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + $branch = "${{ github.ref_name }}" + $prs = gh pr list --state open --head "promote-shipped-apis-$branch" --json number --jq '.[0].number' 2>$null + if ($prs) { + echo "pr_number=$prs" >> $env:GITHUB_OUTPUT + echo "pr_exists=true" >> $env:GITHUB_OUTPUT + Write-Host "Found existing PR: $prs" + } else { + echo "pr_exists=false" >> $env:GITHUB_OUTPUT + Write-Host "No existing PR found" + } + + - name: Checkout existing PR branch + if: steps.check_pr.outputs.pr_exists == 'true' + shell: pwsh + run: | + $branch = "${{ github.ref_name }}" + $prBranch = "promote-shipped-apis-$branch" + + git fetch origin + git checkout $prBranch + + - name: Merge trigger branch into PR branch + if: steps.check_pr.outputs.pr_exists == 'true' + shell: pwsh + run: | + $branch = "${{ github.ref_name }}" + git merge origin/$branch -m "Merge $branch into promote branch" + + - name: Run promote unshipped script + shell: pwsh + run: | + & .\scripts\promoteUnshipped.ps1 + + - name: Check for changes + id: check_changes + shell: pwsh + run: | + $changes = git diff --name-only -- "*Shipped.txt" + if ($changes) { + echo "has_changes=true" >> $env:GITHUB_OUTPUT + Write-Host "Changed files: $changes" + } else { + echo "has_changes=false" >> $env:GITHUB_OUTPUT + Write-Host "No changes detected" + } + + - name: Commit and push changes + if: steps.check_changes.outputs.has_changes == 'true' + shell: pwsh + run: | + git add *hipped.txt + git commit -m "chore: promote shipped APIs" + + $branch = "${{ github.ref_name }}" + $prBranch = "promote-shipped-apis-$branch" + git push -u origin HEAD:$prBranch + + - name: Create new PR + if: steps.check_pr.outputs.pr_exists == 'false' && steps.check_changes.outputs.has_changes == 'true' + shell: pwsh + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + $branch = "${{ github.ref_name }}" + $prBranch = "promote-shipped-apis-$branch" + $title = "automatic promotion of shipped APIs for $branch" + + gh pr create --title "$title" --base "$branch" --head "$prBranch" --body "Automatically promotes unshipped APIs to shipped after running the promotion script." + diff --git a/.github/workflows/release-please-gha.yml b/.github/workflows/release-please-gha.yml index e72901a53..74a6233fb 100644 --- a/.github/workflows/release-please-gha.yml +++ b/.github/workflows/release-please-gha.yml @@ -22,20 +22,53 @@ permissions: contents: read jobs: + check-secret: + runs-on: ubuntu-latest + outputs: + has-token: ${{ steps.check.outputs.has-token }} + steps: + - id: check + run: echo "has-token=${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM != '' }}" >> $GITHUB_OUTPUT release: + needs: check-secret + if: needs.check-secret.outputs.has-token == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Validate PublicAPI.Unshipped.txt files are empty + shell: pwsh + run: | + $unshippedFiles = Get-ChildItem 'src/ ' -Recurse -Filter *.Unshipped.txt | Select -ExpandProperty FullName + + $hasUnshippedAPIs = $false + + foreach ($file in $unshippedFiles) { + $content = Get-Content -Path $file -Raw + # Remove the #nullable enable line and any whitespace + $trimmedContent = $content -replace '^\s*#nullable enable\s*$', '' -replace '^\s+', '' + + if ($trimmedContent -ne '') { + Write-Error "❌ $file contains unshipped APIs. Please promote them to PublicAPI.Shipped.txt before releasing.`nContent:`n$content" + $hasUnshippedAPIs = $true + } + } + + if ($hasUnshippedAPIs) { + exit 1 + } + + Write-Output "✅ All PublicAPI.Unshipped.txt files are clean" - name: Generate GitHub App token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: - app-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} + client-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }} - name: Release Please - uses: googleapis/release-please-action@v4 + uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 with: token: ${{ steps.app-token.outputs.token }} config-file: release-please-config.json diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 51f9b5986..873f235ab 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -35,19 +35,23 @@ jobs: runs-on: windows-latest steps: - name: Set up JDK 17 - uses: actions/setup-java@v5 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: distribution: 'adopt' java-version: 17 - - name: Setup .NET - uses: actions/setup-dotnet@v5 + - name: Setup .NET 8 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 8.0.x - - uses: actions/checkout@v5 + - name: Setup .NET 10 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: 10.0.x + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar diff --git a/Dockerfile b/Dockerfile index fd821e3e4..25f1ec589 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,5 +19,5 @@ VOLUME /app/collection.json ENV HIDI_CONTAINER=true DOTNET_TieredPGO=1 DOTNET_TC_QuickJitForLoops=1 ENTRYPOINT ["dotnet", "Microsoft.OpenApi.Hidi.dll"] LABEL description="# Welcome to Hidi \ -To start transforming OpenAPI documents checkout [the getting started documentation](https://github.com/microsoft/OpenAPI.NET/tree/vnext/src/Microsoft.OpenApi.Hidi) \ -[Source dockerfile](https://github.com/microsoft/OpenAPI.NET/blob/vnext/Dockerfile)" +To start transforming OpenAPI documents checkout [the getting started documentation](https://github.com/microsoft/OpenAPI.NET/tree/main/src/Microsoft.OpenApi.Hidi) \ +[Source dockerfile](https://github.com/microsoft/OpenAPI.NET/blob/main/Dockerfile)" diff --git a/scripts/promoteUnshipped.ps1 b/scripts/promoteUnshipped.ps1 new file mode 100644 index 000000000..4ba6ca4b4 --- /dev/null +++ b/scripts/promoteUnshipped.ps1 @@ -0,0 +1,13 @@ +$nullableConstant = "#nullable enable" +$unshippedDocuments = Get-ChildItem -Filter *.Unshipped* -Recurse | Select-Object -ExpandProperty FullName +foreach ($unshippedDocumentPath in $unshippedDocuments) { + $shippedDocumentPath = $unshippedDocumentPath -replace '\.Unshipped', '.Shipped' + $unshippedDocumentContent = Get-Content $unshippedDocumentPath -Raw + $unshippedDocumentContent = ($unshippedDocumentContent -replace [regex]::Escape($nullableConstant), '').Trim() + if ([string]::IsNullOrWhiteSpace($unshippedDocumentContent)) { + Write-Host "No content to promote for $unshippedDocumentPath, skipping." -ForegroundColor Yellow + continue + } + Add-Content -Path $shippedDocumentPath -Value $unshippedDocumentContent -Verbose + Set-Content -Path $unshippedDocumentPath -Value $nullableConstant -Verbose +} \ No newline at end of file From 785355ceab95c20cc39b5f905532906d246befdb Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 13 Aug 2026 08:09:28 -0400 Subject: [PATCH 2/2] ci: adds missing global.json --- global.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 000000000..d0c1ec64c --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "10.0.302" + } +} \ No newline at end of file