Skip to content

Commit 8ce172a

Browse files
committed
Merge branch 'vnext' into dm/toolcleanup
2 parents 15352bd + a28f5dd commit 8ce172a

File tree

7 files changed

+326
-7
lines changed

7 files changed

+326
-7
lines changed

.github/workflows/ci-cd.yml

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
name: CI/CD Pipeline
2+
3+
on: [push, pull_request, workflow_dispatch]
4+
5+
jobs:
6+
ci:
7+
name: Continuous Integration
8+
runs-on: ubuntu-latest
9+
outputs:
10+
latest_version: ${{ steps.tag_generator.outputs.new_version }}
11+
is_default_branch: ${{ steps.conditionals_handler.outputs.is_default_branch }}
12+
env:
13+
ARTIFACTS_FOLDER: ${{ github.workspace }}/Artifacts
14+
GITHUB_RUN_NUMBER: ${{ github.run_number }}
15+
steps:
16+
- name: Setup .NET
17+
uses: actions/setup-dotnet@v1
18+
with:
19+
dotnet-version: 5.0.x
20+
21+
- name: Data gatherer
22+
id: data_gatherer
23+
shell: pwsh
24+
run: |
25+
# Get default branch
26+
$repo = 'microsoft/OpenAPI.NET'
27+
$defaultBranch = Invoke-RestMethod -Method GET -Uri https://api.github.com/repos/$repo | Select-Object -ExpandProperty default_branch
28+
Write-Output "::set-output name=default_branch::$(echo $defaultBranch)"
29+
30+
- name: Conditionals handler
31+
id: conditionals_handler
32+
shell: pwsh
33+
run: |
34+
$defaultBranch = "${{ steps.data_gatherer.outputs.default_branch }}"
35+
$githubRef = "${{ github.ref }}"
36+
$isDefaultBranch = 'false'
37+
if ( $githubRef -like "*$defaultBranch*" ) {
38+
$isDefaultBranch = 'true'
39+
}
40+
Write-Output "::set-output name=is_default_branch::$(echo $isDefaultBranch)"
41+
42+
- name: Checkout repository
43+
id: checkout_repo
44+
uses: actions/checkout@v2
45+
with:
46+
token: ${{ secrets.GITHUB_TOKEN }}
47+
fetch-depth: 0
48+
49+
- if: steps.conditionals_handler.outputs.is_default_branch == 'true'
50+
name: Bump GH tag
51+
id: tag_generator
52+
uses: mathieudutour/[email protected]
53+
with:
54+
github_token: ${{ secrets.GITHUB_TOKEN }}
55+
default_bump: false
56+
release_branches: ${{ steps.data_gatherer.outputs.default_branch }}
57+
58+
- name: Build projects
59+
id: build_projects
60+
shell: pwsh
61+
run: |
62+
$projectsArray = @(
63+
'.\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj',
64+
'.\src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj',
65+
'.\src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj'
66+
)
67+
$gitNewVersion = if ("${{ steps.tag_generator.outputs.new_version }}") {"${{ steps.tag_generator.outputs.new_version }}"} else {$null}
68+
$projectCurrentVersion = ([xml](Get-Content .\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj)).Project.PropertyGroup.Version
69+
$projectNewVersion = $gitNewVersion ?? $projectCurrentVersion
70+
71+
$projectsArray | ForEach-Object {
72+
dotnet build $PSItem `
73+
-c Release # `
74+
# -o $env:ARTIFACTS_FOLDER `
75+
# /p:Version=$projectNewVersion
76+
}
77+
78+
# Move NuGet packages to separate folder for pipeline convenience
79+
# New-Item Artifacts/NuGet -ItemType Directory
80+
# Get-ChildItem Artifacts/*.nupkg | Move-Item -Destination "Artifacts/NuGet"
81+
82+
- name: Run unit tests
83+
id: run_unit_tests
84+
shell: pwsh
85+
run: |
86+
$testProjectsArray = @(
87+
'.\test\Microsoft.OpenApi.Tests\Microsoft.OpenApi.Tests.csproj',
88+
'.\test\Microsoft.OpenApi.Readers.Tests\Microsoft.OpenApi.Readers.Tests.csproj',
89+
'.\test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj'
90+
)
91+
92+
$testProjectsArray | ForEach-Object {
93+
dotnet test $PSItem `
94+
-c Release
95+
}
96+
97+
# - if: steps.tag_generator.outputs.new_version != ''
98+
# name: Upload NuGet packages as artifacts
99+
# id: ul_packages_artifact
100+
# uses: actions/upload-artifact@v1
101+
# with:
102+
# name: NuGet packages
103+
# path: Artifacts/NuGet/
104+
105+
cd:
106+
if: needs.ci.outputs.is_default_branch == 'true' && needs.ci.outputs.latest_version != ''
107+
name: Continuous Deployment
108+
needs: ci
109+
runs-on: ubuntu-latest
110+
steps:
111+
# - name: Download and extract NuGet packages
112+
# id: dl_packages_artifact
113+
# uses: actions/download-artifact@v2
114+
# with:
115+
# name: NuGet packages
116+
# path: NuGet/
117+
118+
# - name: Push NuGet packages to NuGet.org
119+
# id: push_nuget_packages
120+
# continue-on-error: true
121+
# shell: pwsh
122+
# run: |
123+
# Get-ChildItem NuGet/*.nupkg | ForEach-Object {
124+
# nuget push $PSItem `
125+
# -ApiKey $env:NUGET_API_KEY `
126+
# -Source https://api.nuget.org/v3/index.json
127+
# }
128+
# env:
129+
# NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
130+
131+
- name: Create and publish release
132+
id: create_release
133+
uses: softprops/action-gh-release@v1
134+
with:
135+
name: OpenApi v${{ needs.ci.outputs.latest_version }}
136+
tag_name: v${{ needs.ci.outputs.latest_version }}
137+
# files: |
138+
# NuGet/Microsoft.OpenApi.${{ needs.ci.outputs.latest_version }}.nupkg
139+
# NuGet/Microsoft.OpenApi.Readers.${{ needs.ci.outputs.latest_version }}.nupkg
140+
env:
141+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
142+
143+
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)

docs/CI-CD_DOCUMENTATION.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# CI/CD documentation
2+
3+
## 1. Run workflow manually
4+
5+
1. Go to the project's GitHub repository and click on the **Actions** tab
6+
7+
2. From the "Workflows" list on the left, click on "CI/CD Pipeline"
8+
9+
3. On the right, next to the "This workflow has a workflow_dispatch event trigger" label, click on the "Run workflow" dropdown, make sure the default branch is selected (if not manually changed, should be main or master) in the "Use workflow from" dropdown and click the "Run workflow" button
10+
11+
![Actions_workflow_dispatch](images/Actions_workflow_dispatch.png)
12+
13+
NOTE: **screenshots are only exemplary**
14+
15+
<br>
16+
17+
## 2. Automated NuGet publishing
18+
19+
To setup the automated publishing to NuGet:
20+
21+
1. Go to the repo **Settings** tab -> **Secrets**
22+
23+
2. Add a secret with the name `NUGET_API_KEY` and as value use an API key from NuGet.org that is assigned to the packages for this project
24+
25+
NOTE: the automated NuGet publishing is execute **only** when a release is triggered by the ["Automated versioning" feature](#3-automated-versioning)
26+
27+
<br>
28+
29+
## 3. Automated versioning
30+
31+
Automatically bumps up the GitHub tag in the repo and executes the CD job
32+
33+
Note: **not every commit to your default branch creates a release**
34+
35+
Follow these instructions for any commit (push or PR merge) to your default branch, you would like to execute the automated versioning.
36+
37+
You would need one of three keywords at the start of your commit title. Each of the three keywords corresponds to a number in your release version i.e. v1.2.3. The release versioning uses the ["Conventional Commits" specification](https://www.conventionalcommits.org/en/v1.0.0/):
38+
39+
- "fix: ..." - this keyword corresponds to the last number v1.2.**3**, also known as PATCH;
40+
- "feat: ..." - this keyword corresponds to the middle number v1.**2**.3, also known as MINOR;
41+
- "perf: ..." - this keyword corresponds to the first number v**1**.2.3, also known as MAJOR. In addition, to trigger a MAJOR release, you would need to write "BREAKING CHANGE: ..." in the description of the commit, with an empty line above it to indicate it is in the <footer> portion of the description;
42+
43+
Note: when making a MAJOR release by committing through a terminal, use the multiple line syntax to add the commit title on one line and then adding an empty line, and then adding the "BREAKING CHANGE: " label
44+
<br><br>
45+
46+
#### Examples
47+
48+
Example(fix/PATCH): <br>
49+
`git commit -a -m "fix: this is a PATCH release triggering commit"`
50+
<br>
51+
`git push origin vnext`
52+
<br>
53+
Result: v1.2.3 -> **v1.2.4**
54+
<br>
55+
<br>
56+
<br>
57+
Example(feat/MINOR): <br>
58+
`git commit -a -m "feat: this is a MINOR release triggering commit"`
59+
<br>
60+
`git push origin vnext`
61+
<br>
62+
Result: v1.2.3 -> **v1.3.0**
63+
<br>
64+
<br>
65+
<br>
66+
Example(perf/MAJOR): <br>
67+
`` git commit -a -m "perf: this is a MAJOR release triggering commit ` ``
68+
<br>
69+
&gt;&gt; <br>
70+
&gt;&gt; `BREAKING CHANGE: this is the breaking change"`
71+
<br>
72+
`git push origin vnext`
73+
<br>
74+
Result: v1.2.3 -> **v2.0.0**
75+
<br>
76+
<br>
77+
Note: in the MAJOR release example, the PowerShell multiline syntax ` (backtick) is used. After writing a backtick, a press of the Enter key should open a new line.
78+
79+
#
80+
81+
Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)
38.8 KB
Loading

src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ public static class OpenApiParameterRules
9696
context.Exit();
9797
});
9898

99+
/// <summary>
100+
/// Validate that a path parameter should always appear in the path
101+
/// </summary>
102+
public static ValidationRule<OpenApiParameter> PathParameterShouldBeInThePath =>
103+
new ValidationRule<OpenApiParameter>(
104+
(context, parameter) =>
105+
{
106+
if (parameter.In == ParameterLocation.Path && !context.PathString.Contains("{" + parameter.Name + "}"))
107+
{
108+
context.Enter("in");
109+
context.CreateError(
110+
nameof(PathParameterShouldBeInThePath),
111+
$"Declared path parameter \"{parameter.Name}\" needs to be defined as a path parameter at either the path or operation level");
112+
context.Exit();
113+
}
114+
});
99115
// add more rule.
100116
}
101117
}

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,7 @@ namespace Microsoft.OpenApi.Validations.Rules
11681168
{
11691169
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiParameter> ParameterMismatchedDataType { get; }
11701170
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiParameter> ParameterRequiredFields { get; }
1171+
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiParameter> PathParameterShouldBeInThePath { get; }
11711172
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiParameter> RequiredMustBeTrueWhenInIsPath { get; }
11721173
}
11731174
[Microsoft.OpenApi.Validations.Rules.OpenApiRule]

test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ public void ValidateRequiredIsTrueWhenInIsPathInParameter()
4848
};
4949

5050
// Act
51-
var errors = parameter.Validate(ValidationRuleSet.GetDefaultRuleSet());
52-
51+
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
52+
validator.Enter("{name}");
53+
var walker = new OpenApiWalker(validator);
54+
walker.Walk(parameter);
55+
var errors = validator.Errors;
5356
// Assert
5457
errors.Should().NotBeEmpty();
5558
errors.Select(e => e.Message).Should().BeEquivalentTo(new[]
@@ -77,6 +80,7 @@ public void ValidateExampleShouldNotHaveDataTypeMismatchForSimpleSchema()
7780

7881
// Act
7982
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
83+
validator.Enter("{parameter1}");
8084
var walker = new OpenApiWalker(validator);
8185
walker.Walk(parameter);
8286

@@ -91,7 +95,7 @@ public void ValidateExampleShouldNotHaveDataTypeMismatchForSimpleSchema()
9195
});
9296
errors.Select(e => e.Pointer).Should().BeEquivalentTo(new[]
9397
{
94-
"#/example",
98+
"#/{parameter1}/example",
9599
});
96100
}
97101

@@ -150,6 +154,7 @@ public void ValidateExamplesShouldNotHaveDataTypeMismatchForSimpleSchema()
150154

151155
// Act
152156
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
157+
validator.Enter("{parameter1}");
153158
var walker = new OpenApiWalker(validator);
154159
walker.Walk(parameter);
155160

@@ -168,10 +173,83 @@ public void ValidateExamplesShouldNotHaveDataTypeMismatchForSimpleSchema()
168173
{
169174
// #enum/0 is not an error since the spec allows
170175
// representing an object using a string.
171-
"#/examples/example1/value/y",
172-
"#/examples/example1/value/z",
173-
"#/examples/example2/value"
176+
"#/{parameter1}/examples/example1/value/y",
177+
"#/{parameter1}/examples/example1/value/z",
178+
"#/{parameter1}/examples/example2/value"
174179
});
175180
}
181+
182+
[Fact]
183+
public void PathParameterNotInThePathShouldReturnAnError()
184+
{
185+
// Arrange
186+
IEnumerable<OpenApiError> errors;
187+
188+
var parameter = new OpenApiParameter()
189+
{
190+
Name = "parameter1",
191+
In = ParameterLocation.Path,
192+
Required = true,
193+
Schema = new OpenApiSchema()
194+
{
195+
Type = "string",
196+
}
197+
};
198+
199+
// Act
200+
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
201+
202+
var walker = new OpenApiWalker(validator);
203+
walker.Walk(parameter);
204+
205+
errors = validator.Errors;
206+
bool result = errors.Any();
207+
208+
// Assert
209+
result.Should().BeTrue();
210+
errors.OfType<OpenApiValidatorError>().Select(e => e.RuleName).Should().BeEquivalentTo(new[]
211+
{
212+
"PathParameterShouldBeInThePath"
213+
});
214+
errors.Select(e => e.Pointer).Should().BeEquivalentTo(new[]
215+
{
216+
"#/in"
217+
});
218+
}
219+
220+
[Fact]
221+
public void PathParameterInThePastShouldBeOk()
222+
{
223+
// Arrange
224+
IEnumerable<OpenApiError> errors;
225+
226+
var parameter = new OpenApiParameter()
227+
{
228+
Name = "parameter1",
229+
In = ParameterLocation.Path,
230+
Required = true,
231+
Schema = new OpenApiSchema()
232+
{
233+
Type = "string",
234+
}
235+
};
236+
237+
// Act
238+
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
239+
validator.Enter("paths");
240+
validator.Enter("/{parameter1}");
241+
validator.Enter("get");
242+
validator.Enter("parameters");
243+
validator.Enter("1");
244+
245+
var walker = new OpenApiWalker(validator);
246+
walker.Walk(parameter);
247+
248+
errors = validator.Errors;
249+
bool result = errors.Any();
250+
251+
// Assert
252+
result.Should().BeFalse();
253+
}
176254
}
177255
}

test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules()
4343
Assert.NotEmpty(rules);
4444

4545
// Update the number if you add new default rule(s).
46-
Assert.Equal(20, rules.Count);
46+
Assert.Equal(21, rules.Count);
4747
}
4848
}
4949
}

0 commit comments

Comments
 (0)