diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..12f65c04f --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,63 @@ +# Copilot Instructions + +## Commit Message Format + +Always use conventional commits format when creating commits. Follow this structure: + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +### Types + +- **feat**: A new feature +- **fix**: A bug fix +- **docs**: Documentation only changes +- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc) +- **refactor**: A code change that neither fixes a bug nor adds a feature +- **perf**: A code change that improves performance +- **test**: Adding missing tests or correcting existing tests +- **build**: Changes that affect the build system or external dependencies +- **ci**: Changes to CI configuration files and scripts +- **chore**: Other changes that don't modify src or test files + +### Scope + +The scope should indicate the package or area affected (e.g., `library`, `yaml-reader`, `hidi`). + +### Examples + +``` +feat(library): add support for pattern properties +fix(yaml-reader): updates boolean serialization +docs(README): update installation instructions +ci(release): configure automated release workflow +``` + +### Breaking Changes + +If a commit introduces a breaking change, add `BREAKING CHANGE:` in the footer or append `!` after the type/scope: + +``` +feat(identity-emitter)!: change output format for models + +BREAKING CHANGE: The emitter now generates TypeScript interfaces instead of types +``` + + +## Updating the benchmark information + +The user might request you update the benchmark information. You might do it on your own if a previous change added new properties to models under **src/Microsoft.OpenApi/Models**. Always use a separate commit for this change. + +To do so, run the following script: + +```shell +cd performance/benchmark +dotnet run -c Release +``` + +Then commit the report files using a "chore" commit. \ No newline at end of file diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c13b4d179..df49f2842 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -76,7 +76,7 @@ jobs: working-directory: ./performance/benchmark - name: Publish benchmark results - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: if-no-files-found: error name: benchmark-results diff --git a/.github/workflows/promote-shipped-apis.yml b/.github/workflows/promote-shipped-apis.yml new file mode 100644 index 000000000..aa611b7a0 --- /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@v2 + with: + app-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }} + + - name: Checkout code + uses: actions/checkout@v6 + 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 c905a3f48..6e2c9825d 100644 --- a/.github/workflows/release-please-gha.yml +++ b/.github/workflows/release-please-gha.yml @@ -27,6 +27,30 @@ jobs: steps: - uses: actions/checkout@v6 + - 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 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0befe35d6..2437b4193 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.3.1" + ".": "3.4.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d8605bbf..033c3f970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [3.4.0](https://github.com/microsoft/OpenAPI.NET/compare/v3.3.1...v3.4.0) (2026-03-04) + + +### Features + +* **library:** preserve PatternProperties as x-jsonschema-patternProperties extension for OpenAPI v2/v3.0 serialization ([d969fdc](https://github.com/microsoft/OpenAPI.NET/commit/d969fdc12632ad0879b9fe07d3ab736b21ae68cf)) +* **library:** Preserve PatternProperties via x-jsonschema-patternProperties extension for OpenAPI v2/v3.0 ([16ab5e4](https://github.com/microsoft/OpenAPI.NET/commit/16ab5e42bdef6e08e2afaf7ad3921ac8178f059c)) +* **securityscheme:** add oauth2MetadataUrl support (OpenAPI 3.2) ([4509488](https://github.com/microsoft/OpenAPI.NET/commit/4509488f2e8725e37a60a3b7a3a8a0b6c0c74378)) + + +### Bug Fixes + +* implement unevaluatedProperties as schema per JSON Schema 2020-12 ([#2728](https://github.com/microsoft/OpenAPI.NET/issues/2728)) ([7c13fb3](https://github.com/microsoft/OpenAPI.NET/commit/7c13fb3910685b5c17897cd7799b9ef1bba7f80d)) +* **library:** serialize additionalProperties schema in OpenAPI V2 ([f3165fa](https://github.com/microsoft/OpenAPI.NET/commit/f3165fa874a8bb76788684742492f48b20aab892)) +* **library:** serialize additionalProperties schema in OpenAPI V2 documents ([3d07756](https://github.com/microsoft/OpenAPI.NET/commit/3d0775610a9e74b6d5ce845195ccb935576bcd31)) +* optimize parsing V3.1 documents by reducing GetLocation method allocation on hot path([#2748](https://github.com/microsoft/OpenAPI.NET/issues/2748)) ([f690681](https://github.com/microsoft/OpenAPI.NET/commit/f6906811e616f8e6875b8d839137c846fc7bbd9e)) + ## [3.3.1](https://github.com/microsoft/OpenAPI.NET/compare/v3.3.0...v3.3.1) (2026-01-22) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 195723567..7719a3839 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,4 +74,53 @@ The recommended commit types used are: - __ci__ for CI configuration file changes e.g. updating a pipeline - __chore__ for miscallaneous non-sdk changesin the repo e.g. removing an unused file -Adding an exclamation mark after the commit type (`feat!`) or footer with the prefix __BREAKING CHANGE:__ will cause an increment of the _major_ version. \ No newline at end of file +Adding an exclamation mark after the commit type (`feat!`) or footer with the prefix __BREAKING CHANGE:__ will cause an increment of the _major_ version. + +## Updates to public API surface + +Because we need to maintain a compatible public API surface within a major version, this project is using the public API analyzers to ensure no prior public API is changed/removed inadvertently. + +This means that: + +- All entries in an __Unshipped__ document need to be moved to the __Shipped__ document before any public release. +- All new APIs being added need to be __Unshipped__ document before the pull request can be merged, otherwise build will fail with a message like the example below. + +```txt +Error: /home/runner/work/OpenAPI.NET/OpenAPI.NET/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs(39,46): error RS0016: Symbol 'OAuth2MetadataUrl.set' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/home/runner/work/OpenAPI.NET/OpenAPI.NET/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj::TargetFramework=net8.0] +``` + +### Update the unshipped document + +To update the unshipped document, simply run the following commands + +```shell +# add the missing public api entries +dotnet format --diagnostics RS0016 +# discard changes to cs files to avoid creating conflicts +git checkout *.cs +``` + +### Move items from unshipped to unshipped document + +```pwsh +. ./scripts/promoteUnshipped.ps1 +``` + +> Note: the promotion of APIs is automated through the dedicated workflow and should result in pull requests being automatically opened. + +## Updating the benchmark information + +To ensure performance of the library does not degrade over time, we have continuous benchmarks running. You might see the continuous integration failing if your pull request changed any model under __src/Microsoft.OpenApi/Models__. + +```txt +Benchmark result for EmptyApiSchema does not match the existing benchmark result (original!=new). Allocated bytes differ: 408 != 416 +``` + +To update the benchmarks, run the following script: + +```shell +cd performance/benchmark +dotnet run -c Release +``` + +Then commit the report files using a "chore" commit. diff --git a/Directory.Build.props b/Directory.Build.props index 13c72dd49..9129d4c5e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,13 +12,13 @@ https://github.com/Microsoft/OpenAPI.NET © Microsoft Corporation. All rights reserved. OpenAPI .NET - 3.3.1 + 3.4.0 true - + \ No newline at end of file diff --git a/global.json b/global.json index 4ca757b0e..459fe3e6e 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "8.0.417" + "version": "8.0.418" } } \ No newline at end of file diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md index 993b5da5e..1af965d08 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md @@ -1,18 +1,20 @@ ``` -BenchmarkDotNet v0.15.4, Windows 11 (10.0.26200.6899) -11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores -.NET SDK 8.0.415 - [Host] : .NET 8.0.21 (8.0.21, 8.0.2125.47513), X64 RyuJIT x86-64-v4 - ShortRun : .NET 8.0.21 (8.0.21, 8.0.2125.47513), X64 RyuJIT x86-64-v4 +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7840/25H2/2025Update/HudsonValley2) +AMD Ryzen 7 7800X3D 4.20GHz, 1 CPU, 16 logical and 8 physical cores +.NET SDK 8.0.418 + [Host] : .NET 8.0.24 (8.0.24, 8.0.2426.7010), X64 RyuJIT x86-64-v4 + ShortRun : .NET 8.0.24 (8.0.24, 8.0.2426.7010), X64 RyuJIT x86-64-v4 Job=ShortRun IterationCount=3 LaunchCount=1 WarmupCount=3 ``` -| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | -|------------- |---------------:|---------------:|--------------:|-----------:|-----------:|----------:|-------------:| -| PetStoreYaml | 913.5 μs | 3,348.1 μs | 183.52 μs | 58.5938 | 11.7188 | - | 361.25 KB | -| PetStoreJson | 425.1 μs | 327.5 μs | 17.95 μs | 35.1563 | 5.8594 | - | 223.39 KB | -| GHESYaml | 1,765,825.8 μs | 5,036,336.6 μs | 276,058.60 μs | 60000.0000 | 23000.0000 | 4000.0000 | 345082.98 KB | -| GHESJson | 848,197.4 μs | 1,381,723.6 μs | 75,736.93 μs | 33000.0000 | 12000.0000 | 2000.0000 | 206597.63 KB | +| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|------------- |-------------:|--------------:|-------------:|-----------:|----------:|----------:|-------------:| +| PetStoreYaml | 261.1 μs | 105.89 μs | 5.80 μs | 5.8594 | - | - | 361.38 KB | +| PetStoreJson | 101.9 μs | 48.20 μs | 2.64 μs | 4.3945 | 0.9766 | - | 223.52 KB | +| GHESYaml | 602,932.7 μs | 170,410.86 μs | 9,340.79 μs | 9000.0000 | 8000.0000 | 2000.0000 | 345336.55 KB | +| GHESJson | 254,976.7 μs | 111,875.43 μs | 6,132.27 μs | 4000.0000 | 3000.0000 | 1000.0000 | 206858.06 KB | +| GHESNextYaml | 729,602.0 μs | 357,122.29 μs | 19,575.08 μs | 13000.0000 | 9000.0000 | 2000.0000 | 541566.37 KB | +| GHESNextJson | 378,208.4 μs | 109,458.45 μs | 5,999.79 μs | 8000.0000 | 5000.0000 | 1000.0000 | 406762.41 KB | diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv index ec3b99fae..963b9ce2e 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv @@ -1,5 +1,7 @@ -Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,LargeAddressAware,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Gen0,Gen1,Gen2,Allocated -PetStoreYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,913.5 μs,"3,348.1 μs",183.52 μs,58.5938,11.7188,0.0000,361.25 KB -PetStoreJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,425.1 μs,327.5 μs,17.95 μs,35.1563,5.8594,0.0000,223.39 KB -GHESYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"1,765,825.8 μs","5,036,336.6 μs","276,058.60 μs",60000.0000,23000.0000,4000.0000,345082.98 KB -GHESJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"848,197.4 μs","1,381,723.6 μs","75,736.93 μs",33000.0000,12000.0000,2000.0000,206597.63 KB +Method;Job;AnalyzeLaunchVariance;EvaluateOverhead;MaxAbsoluteError;MaxRelativeError;MinInvokeCount;MinIterationTime;OutlierMode;Affinity;EnvironmentVariables;Jit;LargeAddressAware;Platform;PowerPlanMode;Runtime;AllowVeryLargeObjects;Concurrent;CpuGroups;Force;HeapAffinitizeMask;HeapCount;NoAffinitize;RetainVm;Server;Arguments;BuildConfiguration;Clock;EngineFactory;NuGetReferences;Toolchain;IsMutator;InvocationCount;IterationCount;IterationTime;LaunchCount;MaxIterationCount;MaxWarmupIterationCount;MemoryRandomization;MinIterationCount;MinWarmupIterationCount;RunStrategy;UnrollFactor;WarmupCount;Mean;Error;StdDev;Gen0;Gen1;Gen2;Allocated +PetStoreYaml;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;261.1 μs;105.89 μs;5.80 μs;5.8594;0.0000;0.0000;361.38 KB +PetStoreJson;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;101.9 μs;48.20 μs;2.64 μs;4.3945;0.9766;0.0000;223.52 KB +GHESYaml;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;"602,932.7 μs";"170,410.86 μs";"9,340.79 μs";9000.0000;8000.0000;2000.0000;345336.55 KB +GHESJson;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;"254,976.7 μs";"111,875.43 μs";"6,132.27 μs";4000.0000;3000.0000;1000.0000;206858.06 KB +GHESNextYaml;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;"729,602.0 μs";"357,122.29 μs";"19,575.08 μs";13000.0000;9000.0000;2000.0000;541566.37 KB +GHESNextJson;ShortRun;False;Default;Default;Default;Default;Default;Default;1111111111111111;Empty;RyuJit;Default;X64;8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c;.NET 8.0;False;True;False;True;Default;Default;False;False;False;Default;Default;Default;Default;Default;Default;Default;Default;3;Default;1;Default;Default;Default;Default;Default;Default;16;3;"378,208.4 μs";"109,458.45 μs";"5,999.79 μs";8000.0000;5000.0000;1000.0000;406762.41 KB diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html index feb037f9f..a5a17b789 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html @@ -2,7 +2,7 @@ -performance.Descriptions-20251024-100447 +performance.Descriptions-20260223-221211