From cd77cefeca56e8500fd13d9d99bd92fa09c0112d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 17 Nov 2025 10:09:36 -0500 Subject: [PATCH 1/3] ci: syncs changes from v3 to v2 branch --- .azure-pipelines/ci-build.yml | 3 ++ .github/dependabot.yml | 10 ++-- .../OpenAPI.NET-branch-protection.yml | 36 +++++++++++++ .github/pull_request_template.md | 54 +++++++++++++++++++ .github/release-please.yml | 3 ++ .github/workflows/ci-cd.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release-please-gha.yml | 2 + 8 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 .github/pull_request_template.md diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 1f4c7a41e..8921e7112 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -7,6 +7,7 @@ trigger: include: - main - support/v1 + - support/v2 tags: include: - 'v*' @@ -15,6 +16,7 @@ pr: include: - main - support/v1 + - support/v2 variables: buildPlatform: 'Any CPU' @@ -307,6 +309,7 @@ extends: publishFeedCredentials: 'OpenAPI Nuget Connection' - deployment: create_github_release + condition: and(contains(variables['build.SourceBranch'], 'refs/tags/v'), succeeded()) templateContext: type: releaseJob isProduction: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d82ae27c9..87b987112 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,21 +5,23 @@ updates: directory: "/" open-pull-requests-limit: 10 schedule: - interval: "weekly" + interval: "daily" - package-ecosystem: "nuget" # location of package manifests directory: "/" open-pull-requests-limit: 10 schedule: interval: "daily" + groups: + MicrosoftExtensions: + patterns: + - "Microsoft.Extensions.*" - package-ecosystem: dotnet-sdk directory: / schedule: - interval: weekly - day: wednesday + interval: "daily" ignore: - dependency-name: '*' update-types: - version-update:semver-major - version-update:semver-minor -# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) diff --git a/.github/policies/OpenAPI.NET-branch-protection.yml b/.github/policies/OpenAPI.NET-branch-protection.yml index 6deeb87fa..2d8fd2a73 100644 --- a/.github/policies/OpenAPI.NET-branch-protection.yml +++ b/.github/policies/OpenAPI.NET-branch-protection.yml @@ -81,3 +81,39 @@ configuration: # Restrict who can dismiss pull request reviews. boolean restrictsReviewDismissals: false + - branchNamePattern: support/v2 + # This branch pattern applies to the following branches as of approximately 02/27/2025 15:28:20: + # support/v1 + + # Specifies whether this branch can be deleted. boolean + allowsDeletions: false + # Specifies whether forced pushes are allowed on this branch. boolean + allowsForcePushes: false + # Specifies whether new commits pushed to the matching branches dismiss pull request review approvals. boolean + dismissStaleReviews: true + # Specifies whether admins can overwrite branch protection. boolean + isAdminEnforced: true + # Indicates whether "Require a pull request before merging" is enabled. boolean + requiresPullRequestBeforeMerging: true + # Specifies the number of pull request reviews before merging. int (0-6). Should be null/empty if PRs are not required + requiredApprovingReviewsCount: 1 + # Require review from Code Owners. Requires requiredApprovingReviewsCount. boolean + requireCodeOwnersReview: true + # Are commits required to be signed. boolean. TODO: all contributors must have commit signing on local machines. + requiresCommitSignatures: false + # Are conversations required to be resolved before merging? boolean + requiresConversationResolution: true + # Are merge commits prohibited from being pushed to this branch. boolean + requiresLinearHistory: false + # Required status checks to pass before merging. Values can be any string, but if the value does not correspond to any existing status check, the status check will be stuck on pending for status since nothing exists to push an actual status + requiredStatusChecks: + - license/cla + - CodeQL + - Continuous Integration + # Require branches to be up to date before merging. boolean + requiresStrictStatusChecks: false + # Indicates whether there are restrictions on who can push. boolean. Should be set with whoCanPush. + restrictsPushes: false + # Restrict who can dismiss pull request reviews. boolean + restrictsReviewDismissals: false + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..0d19e6ac4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,54 @@ +# Pull Request + + + +## Description + + +## Type of Change + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update +- [ ] Other (please describe): + +## Related Issue(s) + + +## Changes Made + +- +- +- + +## Testing + +- [ ] Unit tests added/updated +- [ ] Integration tests added/updated +- [ ] Manual testing performed +- [ ] All existing tests pass + +## Checklist + +- [ ] My code follows the code style of this project +- [ ] I have performed a self-review of my own code +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes + +## Versions applicability + +- [ ] My change applies to the version 1.X of the library, if so PR link: +- [ ] My change applies to the version 2.X of the library, if so PR link: +- [ ] My change applies to the version 3.X of the library, if so PR link: +- [ ] I have evaluated the applicability of my change against the other versions above. + +See [the contributing guidelines](https://github.com/microsoft/OpenAPI.NET/blob/main/CONTRIBUTING.md) for more information about how patches are applied across multiple versions. + +## Additional Notes + \ No newline at end of file diff --git a/.github/release-please.yml b/.github/release-please.yml index c821fc166..ce23f6ec0 100644 --- a/.github/release-please.yml +++ b/.github/release-please.yml @@ -3,5 +3,8 @@ primaryBranch: main handleGHRelease: true branches: - branch: support/v1 + manifest: true + handleGHRelease: true + - branch: support/v2 manifest: true handleGHRelease: true \ No newline at end of file diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 4135aabc2..9d55602b3 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@v4 + uses: actions/upload-artifact@v5 with: if-no-files-found: error name: benchmark-results diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a69dbcb16..72eea8bbf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,7 +2,7 @@ name: CodeQL Analysis on: push: - branches: [ main, support/v1 ] + branches: [ main, support/v1, support/v2 ] pull_request: schedule: - cron: '0 8 * * *' diff --git a/.github/workflows/release-please-gha.yml b/.github/workflows/release-please-gha.yml index d24634b15..e72901a53 100644 --- a/.github/workflows/release-please-gha.yml +++ b/.github/workflows/release-please-gha.yml @@ -15,6 +15,8 @@ on: branches: - main - support/v1 + - support/v2 + workflow_dispatch: permissions: contents: read From 5230e28b835b081e32481bc724b9f23a16fdd019 Mon Sep 17 00:00:00 2001 From: "release-please-token-provider[bot]" <225477224+release-please-token-provider[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:26:28 +0000 Subject: [PATCH 2/3] chore(support/v2): release 2.3.10 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ Directory.Build.props | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3aedaf18b..380448567 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.3.9" + ".": "2.3.10" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f97968659..4c809876c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [2.3.10](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.9...v2.3.10) (2025-11-17) + + +### Bug Fixes + +* empty strings should be quoted in yaml ([e919b33](https://github.com/microsoft/OpenAPI.NET/commit/e919b33e9d09159217066248483ef4c767865c82)) +* empty strings should be quoted in yaml ([0ca10db](https://github.com/microsoft/OpenAPI.NET/commit/0ca10db3bb9ffa937dd35862068926f3586d6991)) + ## [2.3.9](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.8...v2.3.9) (2025-11-06) diff --git a/Directory.Build.props b/Directory.Build.props index 8ef14df8e..3f2536c56 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,7 @@ https://github.com/Microsoft/OpenAPI.NET © Microsoft Corporation. All rights reserved. OpenAPI .NET - 2.3.9 + 2.3.10 From 946cba992a2733a60182453e38722b4ed789b729 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 8 Dec 2025 09:36:25 -0500 Subject: [PATCH 3/3] fix: additional properties serialization should not emit a schema in v2 fix: additional properties serialization should not emit booleans in v3.1+ Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 18 ++- .../Models/OpenApiSchemaTests.cs | 106 ++++++++++++++++++ 2 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index db3064b00..f31644c1f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -466,17 +466,20 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version writer.WriteOptionalMap(OpenApiConstants.Properties, Properties, callback); // additionalProperties - if (AdditionalPropertiesAllowed) + if (AdditionalProperties is not null && version >= OpenApiSpecVersion.OpenApi3_0) { writer.WriteOptionalObject( OpenApiConstants.AdditionalProperties, AdditionalProperties, callback); } - else + // true is the default in earlier versions 3, no need to write it out + // boolean value is only supported for version 3 and earlier (version 2 is implemented in the other serialize method, the condition is a failsafe) + else if (!AdditionalPropertiesAllowed && version <= OpenApiSpecVersion.OpenApi3_0) { writer.WriteProperty(OpenApiConstants.AdditionalProperties, AdditionalPropertiesAllowed); } + // not having anything is the same as having it set to true (v2/v3) or an empty schema (v3.1+) // description writer.WriteProperty(OpenApiConstants.Description, Description); @@ -727,14 +730,9 @@ private void SerializeAsV2( }); // additionalProperties - if (AdditionalPropertiesAllowed) - { - writer.WriteOptionalObject( - OpenApiConstants.AdditionalProperties, - AdditionalProperties, - (w, s) => s.SerializeAsV2(w)); - } - else + // a schema cannot be serialized in v2 + // true is the default, no need to write it out + if (!AdditionalPropertiesAllowed) { writer.WriteProperty(OpenApiConstants.AdditionalProperties, AdditionalPropertiesAllowed); } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index 87dc85ced..6ffbb9fe4 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -684,6 +684,112 @@ public async Task SerializeConstAsEnumV20() Assert.False(v2Node.AsObject().ContainsKey("const")); } + [Fact] + public async Task SerializeAdditionalPropertiesAsV2DoesNotEmit() + { + var expected = @"{ }"; + // Given + var schema = new OpenApiSchema + { + AdditionalProperties = new OpenApiSchema() + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Fact] + public async Task SerializeAdditionalPropertiesAllowedAsV2DefaultDoesNotEmit() + { + var expected = @"{ }"; + // Given + var schema = new OpenApiSchema + { + AdditionalPropertiesAllowed = true + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Fact] + public async Task SerializeAdditionalPropertiesAllowedAsV2FalseEmits() + { + var expected = @"{ ""additionalProperties"": false }"; + // Given + var schema = new OpenApiSchema + { + AdditionalPropertiesAllowed = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Theory] + [InlineData(OpenApiSpecVersion.OpenApi3_0)] + [InlineData(OpenApiSpecVersion.OpenApi3_1)] + public async Task SerializeAdditionalPropertiesAllowedAsV3PlusDefaultDoesNotEmit(OpenApiSpecVersion version) + { + var expected = @"{ }"; + // Given + var schema = new OpenApiSchema + { + AdditionalPropertiesAllowed = true + }; + + // When + var actual = await schema.SerializeAsJsonAsync(version); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Fact] + public async Task SerializeAdditionalPropertiesAllowedAsV3FalseEmits() + { + var expected = @"{ ""additionalProperties"": false }"; + // Given + var schema = new OpenApiSchema + { + AdditionalPropertiesAllowed = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Theory] + [InlineData(OpenApiSpecVersion.OpenApi3_0)] + [InlineData(OpenApiSpecVersion.OpenApi3_1)] + public async Task SerializeAdditionalPropertiesAsV3PlusEmits(OpenApiSpecVersion version) + { + var expected = @"{ ""additionalProperties"": { } }"; + // Given + var schema = new OpenApiSchema + { + AdditionalProperties = new OpenApiSchema() + }; + + // When + var actual = await schema.SerializeAsJsonAsync(version); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + internal class SchemaVisitor : OpenApiVisitorBase {