diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml new file mode 100644 index 000000000..5649e50c5 --- /dev/null +++ b/.azure-pipelines/ci-build.yml @@ -0,0 +1,126 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) + +trigger: + branches: + include: + - master +pr: none + +pool: + name: Azure Pipelines + vmImage: windows-latest + demands: + - msbuild + - vstest + +steps: +- task: NuGetCommand@2 + displayName: 'NuGet restore' + +- task: MSBuild@1 + displayName: 'Build solution **/*.sln' + inputs: + configuration: Release + +- task: VSTest@2 + displayName: 'XUnit Tests' + inputs: + testAssemblyVer2: | + **\*.Tests.dll + + vsTestVersion: 16.0 + codeCoverageEnabled: true + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: 'ESRP CodeSigning' + inputs: + ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' + FolderPath: src + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "Microsoft" + }, + { + "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" + } + ] + SessionTimeout: 20 + +- task: MSBuild@1 + displayName: 'Pack OpenAPI' + inputs: + solution: src/Microsoft.OpenApi/Microsoft.OpenApi.csproj + configuration: Release + msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' + +- task: MSBuild@1 + displayName: 'Pack OpenAPI Readers' + inputs: + solution: src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj + configuration: Release + msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: 'ESRP CodeSigning Nuget Packages' + inputs: + ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' + FolderPath: '$(Build.ArtifactStagingDirectory)' + Pattern: '*.nupkg' + 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" + } + ] + SessionTimeout: 20 + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: Nugets' + inputs: + ArtifactName: Nugets \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..f3952729f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + # default location of `.github/workflows` + directory: "/" + open-pull-requests-limit: 10 + schedule: + interval: "weekly" + + - package-ecosystem: "nuget" + # location of package manifests + directory: "/" + open-pull-requests-limit: 10 + schedule: + interval: "daily" + +# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 000000000..4ab9ed7c3 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,143 @@ +name: CI/CD Pipeline + +on: [push, pull_request, workflow_dispatch] + +jobs: + ci: + name: Continuous Integration + runs-on: ubuntu-latest + outputs: + latest_version: ${{ steps.tag_generator.outputs.new_version }} + is_default_branch: ${{ steps.conditionals_handler.outputs.is_default_branch }} + env: + ARTIFACTS_FOLDER: ${{ github.workspace }}/Artifacts + GITHUB_RUN_NUMBER: ${{ github.run_number }} + steps: + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 5.0.x + + - name: Data gatherer + id: data_gatherer + shell: pwsh + run: | + # Get default branch + $repo = 'microsoft/OpenAPI.NET' + $defaultBranch = Invoke-RestMethod -Method GET -Uri https://api.github.com/repos/$repo | Select-Object -ExpandProperty default_branch + Write-Output "::set-output name=default_branch::$(echo $defaultBranch)" + + - name: Conditionals handler + id: conditionals_handler + shell: pwsh + run: | + $defaultBranch = "${{ steps.data_gatherer.outputs.default_branch }}" + $githubRef = "${{ github.ref }}" + $isDefaultBranch = 'false' + if ( $githubRef -like "*$defaultBranch*" ) { + $isDefaultBranch = 'true' + } + Write-Output "::set-output name=is_default_branch::$(echo $isDefaultBranch)" + + - name: Checkout repository + id: checkout_repo + uses: actions/checkout@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - if: steps.conditionals_handler.outputs.is_default_branch == 'true' + name: Bump GH tag + id: tag_generator + uses: mathieudutour/github-tag-action@v5.4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + default_bump: false + release_branches: ${{ steps.data_gatherer.outputs.default_branch }} + + - name: Build projects + id: build_projects + shell: pwsh + run: | + $projectsArray = @( + '.\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj', + '.\src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj', + '.\src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj' + ) + $gitNewVersion = if ("${{ steps.tag_generator.outputs.new_version }}") {"${{ steps.tag_generator.outputs.new_version }}"} else {$null} + $projectCurrentVersion = ([xml](Get-Content .\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj)).Project.PropertyGroup.Version + $projectNewVersion = $gitNewVersion ?? $projectCurrentVersion + + $projectsArray | ForEach-Object { + dotnet build $PSItem ` + -c Release # ` + # -o $env:ARTIFACTS_FOLDER ` + # /p:Version=$projectNewVersion + } + + # Move NuGet packages to separate folder for pipeline convenience + # New-Item Artifacts/NuGet -ItemType Directory + # Get-ChildItem Artifacts/*.nupkg | Move-Item -Destination "Artifacts/NuGet" + + - name: Run unit tests + id: run_unit_tests + shell: pwsh + run: | + $testProjectsArray = @( + '.\test\Microsoft.OpenApi.Tests\Microsoft.OpenApi.Tests.csproj', + '.\test\Microsoft.OpenApi.Readers.Tests\Microsoft.OpenApi.Readers.Tests.csproj', + '.\test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj' + ) + + $testProjectsArray | ForEach-Object { + dotnet test $PSItem ` + -c Release + } + + # - if: steps.tag_generator.outputs.new_version != '' + # name: Upload NuGet packages as artifacts + # id: ul_packages_artifact + # uses: actions/upload-artifact@v1 + # with: + # name: NuGet packages + # path: Artifacts/NuGet/ + + cd: + if: needs.ci.outputs.is_default_branch == 'true' && needs.ci.outputs.latest_version != '' + name: Continuous Deployment + needs: ci + runs-on: ubuntu-latest + steps: + # - name: Download and extract NuGet packages + # id: dl_packages_artifact + # uses: actions/download-artifact@v2 + # with: + # name: NuGet packages + # path: NuGet/ + + # - name: Push NuGet packages to NuGet.org + # id: push_nuget_packages + # continue-on-error: true + # shell: pwsh + # run: | + # Get-ChildItem NuGet/*.nupkg | ForEach-Object { + # nuget push $PSItem ` + # -ApiKey $env:NUGET_API_KEY ` + # -Source https://api.nuget.org/v3/index.json + # } + # env: + # NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + + - name: Create and publish release + id: create_release + uses: softprops/action-gh-release@v1 + with: + name: OpenApi v${{ needs.ci.outputs.latest_version }} + tag_name: v${{ needs.ci.outputs.latest_version }} + # files: | + # NuGet/Microsoft.OpenApi.${{ needs.ci.outputs.latest_version }}.nupkg + # NuGet/Microsoft.OpenApi.Readers.${{ needs.ci.outputs.latest_version }}.nupkg + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..95c813772 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,47 @@ +name: CodeQL Analysis + +on: + push: + pull_request: + schedule: + - cron: '0 8 * * *' + +jobs: + analyze: + name: CodeQL Analysis + runs-on: ubuntu-latest + steps: + - name: Checkout repository + id: checkout_repo + uses: actions/checkout@v2 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 5.0.x + + - name: Initialize CodeQL + id: init_codeql + uses: github/codeql-action/init@v1 + with: + queries: security-and-quality + + - name: Build projects + id: build_projects + shell: pwsh + run: | + $projectsArray = @( + '.\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj', + '.\src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj', + '.\src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj' + ) + + $projectsArray | ForEach-Object { + dotnet build $PSItem -c Release + } + + - name: Perform CodeQL Analysis + id: analyze_codeql + uses: github/codeql-action/analyze@v1 + +# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..0d20a9b46 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/Microsoft.OpenApi.Tool/bin/Debug/netcoreapp3.1/Microsoft.OpenApi.Tool.dll", + "args": [], + "cwd": "${workspaceFolder}/src/Microsoft.OpenApi.Tool", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0313280bf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#03323C", + "titleBar.activeBackground": "#054754", + "titleBar.activeForeground": "#F0FCFE" + } +} \ No newline at end of file diff --git a/README.md b/README.md index bce79b6a3..9405526bf 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The **OpenAPI.NET** SDK contains a useful object model for OpenAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model. -**See more information on the OpenAPI specification and its history here: Open API Initiative** +**See more information on the OpenAPI specification and its history here: OpenAPI Initiative** Project Objectives diff --git a/build.cmd b/build.cmd index 3c65e48bd..b3612c9ed 100644 --- a/build.cmd +++ b/build.cmd @@ -13,8 +13,11 @@ dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts -goto :end -:error -echo Version parameter missing e.g. build.cmd 1.0.0-beta0008 +Echo Building Microsoft.OpenApi.Tool + +SET PROJ=%~dp0src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj +dotnet msbuild %PROJ% /t:restore /p:Configuration=Release +dotnet msbuild %PROJ% /t:build /p:Configuration=Release +dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts :end diff --git a/docs/CI-CD_DOCUMENTATION.md b/docs/CI-CD_DOCUMENTATION.md new file mode 100644 index 000000000..40053cf82 --- /dev/null +++ b/docs/CI-CD_DOCUMENTATION.md @@ -0,0 +1,81 @@ +# CI/CD documentation + +## 1. Run workflow manually + +1. Go to the project's GitHub repository and click on the **Actions** tab + +2. From the "Workflows" list on the left, click on "CI/CD Pipeline" + +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 + +![Actions_workflow_dispatch](images/Actions_workflow_dispatch.png) + +NOTE: **screenshots are only exemplary** + +
+ +## 2. Automated NuGet publishing + +To setup the automated publishing to NuGet: + +1. Go to the repo **Settings** tab -> **Secrets** + +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 + +NOTE: the automated NuGet publishing is execute **only** when a release is triggered by the ["Automated versioning" feature](#3-automated-versioning) + +
+ +## 3. Automated versioning + +Automatically bumps up the GitHub tag in the repo and executes the CD job + +Note: **not every commit to your default branch creates a release** + +Follow these instructions for any commit (push or PR merge) to your default branch, you would like to execute the automated versioning. + +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/): + +- "fix: ..." - this keyword corresponds to the last number v1.2.**3**, also known as PATCH; +- "feat: ..." - this keyword corresponds to the middle number v1.**2**.3, also known as MINOR; +- "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