diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..e66c170aa8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,38 @@ +# ------------------------------------------------------------------------------ +# +# +# This code was generated. +# +# - To turn off auto-generation set: +# +# [GitHubActions (AutoGenerate = false)] +# +# - To trigger manual generation invoke: +# +# nuke --generate-configuration GitHubActions_build --host GitHubActions +# +# +# ------------------------------------------------------------------------------ + +name: build + +on: + push: + branches: + - master + +jobs: + windows-latest: + name: windows-latest + runs-on: windows-latest + steps: + - uses: actions/checkout@v1 + - name: Cache .nuke/temp, ~/.nuget/packages + uses: actions/cache@v2 + with: + path: | + .nuke/temp + ~/.nuget/packages + key: ${{ runner.os }}-${{ hashFiles('global.json', 'src/**/*.csproj', 'src/**/package.json') }} + - name: Run './build.cmd InstallDependencies Compile Test Pack' + run: ./build.cmd InstallDependencies Compile Test Pack diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000000..1f79379b16 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,38 @@ +# ------------------------------------------------------------------------------ +# +# +# This code was generated. +# +# - To turn off auto-generation set: +# +# [GitHubActions (AutoGenerate = false)] +# +# - To trigger manual generation invoke: +# +# nuke --generate-configuration GitHubActions_pr --host GitHubActions +# +# +# ------------------------------------------------------------------------------ + +name: pr + +on: + pull_request: + branches: + - master + +jobs: + windows-latest: + name: windows-latest + runs-on: windows-latest + steps: + - uses: actions/checkout@v1 + - name: Cache .nuke/temp, ~/.nuget/packages + uses: actions/cache@v2 + with: + path: | + .nuke/temp + ~/.nuget/packages + key: ${{ runner.os }}-${{ hashFiles('global.json', 'src/**/*.csproj', 'src/**/package.json') }} + - name: Run './build.cmd InstallDependencies Compile Test Pack' + run: ./build.cmd InstallDependencies Compile Test Pack diff --git a/.gitignore b/.gitignore index 546d4d9dc8..cb81466a9d 100644 --- a/.gitignore +++ b/.gitignore @@ -74,5 +74,9 @@ src/packages/** /src/NSwag.Console/Properties/launchSettings.json # Ignore files from JetBrainds Rider -/src/.idea/ +.idea/ _ReSharper.Caches/ + +# NUKE build temp files +.nuke/temp + diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json new file mode 100644 index 0000000000..741c6b3b5b --- /dev/null +++ b/.nuke/build.schema.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Build Schema", + "$ref": "#/definitions/build", + "definitions": { + "build": { + "type": "object", + "properties": { + "Configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", + "enum": [ + "Debug", + "Release" + ] + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "type": "string", + "description": "Host for execution. Default is 'automatic'", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "InstallDependencies", + "IntegrationTest", + "Pack", + "Publish", + "Restore", + "Samples", + "Test", + "UnitTest" + ] + } + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "InstallDependencies", + "IntegrationTest", + "Pack", + "Publish", + "Restore", + "Samples", + "Test", + "UnitTest" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] + } + } + } + } +} \ No newline at end of file diff --git a/.nuke/parameters.json b/.nuke/parameters.json new file mode 100644 index 0000000000..927faa3724 --- /dev/null +++ b/.nuke/parameters.json @@ -0,0 +1,4 @@ +{ + "$schema": "./build.schema.json", + "Solution": "src/NSwag.sln" +} \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100755 index 0000000000..b08cc590f4 --- /dev/null +++ b/build.cmd @@ -0,0 +1,7 @@ +:; set -eo pipefail +:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +:; ${SCRIPT_DIR}/build.sh "$@" +:; exit $? + +@ECHO OFF +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000..1c774e5254 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,69 @@ +[CmdletBinding()] +Param( + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$BuildArguments +) + +Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" + +Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + +########################################################################### +# CONFIGURATION +########################################################################### + +$BuildProjectFile = "$PSScriptRoot\build\_build.csproj" +$TempDirectory = "$PSScriptRoot\\.nuke\temp" + +$DotNetGlobalFile = "$PSScriptRoot\\global.json" +$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" +$DotNetChannel = "Current" + +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 +$env:DOTNET_MULTILEVEL_LOOKUP = 0 + +########################################################################### +# EXECUTION +########################################################################### + +function ExecSafe([scriptblock] $cmd) { + & $cmd + if ($LASTEXITCODE) { exit $LASTEXITCODE } +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` + $(dotnet --version) -and $LASTEXITCODE -eq 0) { + $env:DOTNET_EXE = (Get-Command "dotnet").Path +} +else { + # Download install script + $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" + New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) + + # If global.json exists, load expected version + if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } + } + + # Install by channel or version + $DotNetDirectory = "$TempDirectory\dotnet-win" + if (!(Test-Path variable:DotNetVersion)) { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + } else { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + } + $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" +} + +Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" + +ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } +ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..e8961f9988 --- /dev/null +++ b/build.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +bash --version 2>&1 | head -n 1 + +set -eo pipefail +SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + +########################################################################### +# CONFIGURATION +########################################################################### + +BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" +TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" + +DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" +DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" +DOTNET_CHANNEL="Current" + +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_MULTILEVEL_LOOKUP=0 + +########################################################################### +# EXECUTION +########################################################################### + +function FirstJsonValue { + perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then + export DOTNET_EXE="$(command -v dotnet)" +else + # Download install script + DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" + mkdir -p "$TEMP_DIRECTORY" + curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" + chmod +x "$DOTNET_INSTALL_FILE" + + # If global.json exists, load expected version + if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then + DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") + if [[ "$DOTNET_VERSION" == "" ]]; then + unset DOTNET_VERSION + fi + fi + + # Install by channel or version + DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" + if [[ -z ${DOTNET_VERSION+x} ]]; then + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path + else + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path + fi + export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" +fi + +echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" + +"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet +"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" diff --git a/build/00_Install.bat b/build/00_Install.bat index 34c8edf729..9004a13580 100644 --- a/build/00_Install.bat +++ b/build/00_Install.bat @@ -1,7 +1,3 @@ -pushd "%~dp0\..\src\NSwag.Npm" -cmd /c call npm install +pushd "%~dp0\.." +cmd /c call build.cmd install --configuration Release popd - -pushd "%~dp0\..\src\NSwag.Integration.TypeScriptWeb" -cmd /c call npm install -popd \ No newline at end of file diff --git a/build/01_Build.bat b/build/01_Build.bat index eb79615f52..38c8bdeb40 100644 --- a/build/01_Build.bat +++ b/build/01_Build.bat @@ -1,28 +1,3 @@ -rmdir "%~dp0\..\src\NSwag.Npm\bin\binaries" /Q /S nonemptydir -mkdir "%~dp0\..\src\NSwag.Npm\bin\binaries" - -REM Build and copy full .NET command line -"%~dp0/nuget.exe" restore "%~dp0/../src/NSwag.sln" -dotnet restore "%~dp0/../src/NSwag.sln" -msbuild "%~dp0/../src/NSwag.sln" /p:Configuration=Release || goto :error - -xcopy "%~dp0/../src/NSwag.Console/bin/Release/net461" "%~dp0/../src/NSwag.Npm/bin/binaries/Win" /E /I /y -xcopy "%~dp0\..\src\NSwag.Console.x86\bin\Release\net461\NSwag.x86.exe" "%~dp0\..\src\NSwag.Npm\bin\binaries\Win" -xcopy "%~dp0\..\src\NSwag.Console.x86\bin\Release\net461\NSwag.x86.exe.config" "%~dp0\..\src\NSwag.Npm\bin\binaries\Win" - -REM Build and publish .NET Core command line done in prebuild event for NSwagStudio.Installer.wixproj -xcopy "%~dp0/../src/NSwag.ConsoleCore/bin/release/netcoreapp2.1/publish" "%~dp0/../src/NSwag.Npm/bin/binaries/NetCore21" /E /I /y -xcopy "%~dp0/../src/NSwag.ConsoleCore/bin/release/netcoreapp2.2/publish" "%~dp0/../src/NSwag.Npm/bin/binaries/NetCore22" /E /I /y -xcopy "%~dp0/../src/NSwag.ConsoleCore/bin/release/netcoreapp3.0/publish" "%~dp0/../src/NSwag.Npm/bin/binaries/NetCore30" /E /I /y -xcopy "%~dp0/../src/NSwag.ConsoleCore/bin/release/netcoreapp3.1/publish" "%~dp0/../src/NSwag.Npm/bin/binaries/NetCore31" /E /I /y -xcopy "%~dp0/../src/NSwag.ConsoleCore/bin/release/net5.0/publish" "%~dp0/../src/NSwag.Npm/bin/binaries/Net50" /E /I /y - -REM Package nuspecs -"%~dp0/nuget.exe" pack "%~dp0/../src/NSwag.MSBuild/NSwag.MSBuild.nuspec" || goto :error -"%~dp0/nuget.exe" pack "%~dp0/../src/NSwag.ApiDescription.Client/NSwag.ApiDescription.Client.nuspec" || goto :error -"%~dp0/nuget.exe" pack "%~dp0/../src/NSwagStudio.Chocolatey/NSwagStudio.nuspec" || goto :error - -goto :EOF -:error -echo Failed with error #%errorlevel%. -exit /b %errorlevel% +pushd "%~dp0\.." +cmd /c call build.cmd compile pack --configuration Release +popd diff --git a/build/02_RunUnitTests.bat b/build/02_RunUnitTests.bat index 84b33d3ddd..ad1aff2464 100644 --- a/build/02_RunUnitTests.bat +++ b/build/02_RunUnitTests.bat @@ -1,15 +1,3 @@ -vstest.console /logger:Appveyor "%~dp0../src/NSwag.Generation.WebApi.Tests/bin/Release/NSwag.Generation.WebApi.Tests.dll" || goto :error -REM vstest.console /logger:Appveyor "%~dp0../src/NSwag.Tests/bin/Release/NSwag.Tests.dll" || goto :error - -dotnet test "%~dp0/../src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.CodeGeneration.CSharp.Tests/NSwag.CodeGeneration.CSharp.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.CodeGeneration.TypeScript.Tests/NSwag.CodeGeneration.TypeScript.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.Core.Tests/NSwag.Core.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.Core.Yaml.Tests/NSwag.Core.Yaml.Tests.csproj" -c Release || goto :error -dotnet test "%~dp0/../src/NSwag.AssemblyLoader.Tests/NSwag.AssemblyLoader.Tests.csproj" -c Release -f netcoreapp2.1 || goto :error - -goto :EOF -:error -echo Failed with error #%errorlevel%. -exit /b %errorlevel% +pushd "%~dp0\.." +cmd /c call build.cmd unittest --configuration Release +popd diff --git a/build/03_RunIntegrationTests.bat b/build/03_RunIntegrationTests.bat index 833b32c8f5..fc9fb6b435 100644 --- a/build/03_RunIntegrationTests.bat +++ b/build/03_RunIntegrationTests.bat @@ -1,53 +1,3 @@ -REM pushd "%~dp0\..\src\NSwag.Sample.NetCoreAngular" -REM dotnet publish || goto :error -REM "%~dp0\..src\NSwagStudio\bin\Release\nswag" run /runtime:NetCore11 || goto :error -REM popd - -pushd "%~dp0\..\samples" -cmd /c call powershell .\run.ps1 Release || goto :error +pushd "%~dp0\.." +cmd /c call build.cmd integrationtest --configuration Release popd - -REM pushd "%~dp0\..\src\NSwag.Sample.NETCore11" -REM dotnet restore || goto :error -REM dotnet publish || goto :error -REM cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:NetCore21 || goto :error -REM popd - -REM pushd "%~dp0\..\src\NSwag.Sample.NETCore20" -REM dotnet restore || goto :error -REM dotnet publish || goto :error -REM cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:NetCore21 || goto :error -REM popd - -pushd "%~dp0\..\src\NSwag.Sample.NETCore21" -dotnet restore || goto :error -dotnet build /p:CopyLocalLockFileAssemblies=true || goto :error -cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:NetCore21 || goto :error -popd - -REM pushd "%~dp0\..\src\NSwag.Sample.NETCore22" -REM dotnet restore || goto :error -REM dotnet build /p:CopyLocalLockFileAssemblies=true || goto :error -REM cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:NetCore22 || goto :error -REM popd - -pushd "%~dp0\..\src\NSwag.Sample.NETCore31" -dotnet restore || goto :error -dotnet build /p:CopyLocalLockFileAssemblies=true || goto :error -cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:NetCore31 || goto :error -popd - -pushd "%~dp0\..\src\NSwag.Sample.NetGlobalAsax" -msbuild || goto :error -cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:Winx64 || goto :error -popd - -pushd "%~dp0\..\src\NSwag.Integration.WebAPI" -msbuild || goto :error -cmd /c call "..\NSwagStudio\bin\Release\nswag.cmd" run /runtime:Winx64 || goto :error -popd - -goto :EOF -:error -echo Failed with error #%errorlevel%. -exit /b %errorlevel% diff --git a/build/04_Publish.bat b/build/04_Publish.bat index fc341cb390..64335a3fa3 100644 --- a/build/04_Publish.bat +++ b/build/04_Publish.bat @@ -1 +1,3 @@ -npm publish "%~dp0/../src/NSwag.Npm" \ No newline at end of file +pushd "%~dp0\.." +cmd /c call build.cmd publish --configuration Release +popd diff --git a/build/Build.CI.GitHubActions.cs b/build/Build.CI.GitHubActions.cs new file mode 100644 index 0000000000..dd5cc004f4 --- /dev/null +++ b/build/Build.CI.GitHubActions.cs @@ -0,0 +1,29 @@ +using Nuke.Common.CI.GitHubActions; + +[GitHubActions( + "pr", + GitHubActionsImage.WindowsLatest, + // GitHubActionsImage.UbuntuLatest, + // GitHubActionsImage.MacOsLatest, + OnPullRequestBranches = new [] { "master" }, + // OnPushBranchesIgnore = new[] { MasterBranch, ReleaseBranchPrefix + "/*" }, + //OnPullRequestBranches = new[] { DevelopBranch }, + PublishArtifacts = false, + InvokedTargets = new[] { nameof(InstallDependencies), nameof(Compile), nameof(Test), nameof(Pack) }, + CacheKeyFiles = new[] { "global.json", "src/**/*.csproj", "src/**/package.json" }) +] +[GitHubActions( + "build", + GitHubActionsImage.WindowsLatest, + // GitHubActionsImage.UbuntuLatest, + // GitHubActionsImage.MacOsLatest, + OnPushBranches = new [] { "master" }, + // OnPushBranchesIgnore = new[] { MasterBranch, ReleaseBranchPrefix + "/*" }, + //OnPullRequestBranches = new[] { DevelopBranch }, + PublishArtifacts = false, + InvokedTargets = new[] { nameof(InstallDependencies), nameof(Compile), nameof(Test), nameof(Pack) }, + CacheKeyFiles = new[] { "global.json", "src/**/*.csproj", "src/**/package.json" }) +] +public partial class Build +{ +} diff --git a/build/Build.cs b/build/Build.cs new file mode 100644 index 0000000000..fbf958b3b2 --- /dev/null +++ b/build/Build.cs @@ -0,0 +1,311 @@ +using System; +using System.IO; +using Nuke.Common; +using Nuke.Common.CI.AppVeyor; +using Nuke.Common.Execution; +using Nuke.Common.Git; +using Nuke.Common.IO; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; +using Nuke.Common.Tools.DotNet; +using Nuke.Common.Tools.MSBuild; +using Nuke.Common.Tools.Npm; +using Nuke.Common.Tools.NuGet; +using Nuke.Common.Tools.VSTest; +using Nuke.Common.Utilities.Collections; + +using static Nuke.Common.IO.FileSystemTasks; +using static Nuke.Common.Logger; +using static Nuke.Common.Tooling.ProcessTasks; +using static Nuke.Common.Tools.Chocolatey.ChocolateyTasks; +using static Nuke.Common.Tools.DotNet.DotNetTasks; +using static Nuke.Common.Tools.MSBuild.MSBuildTasks; +using static Nuke.Common.Tools.Npm.NpmTasks; +using static Nuke.Common.Tools.NuGet.NuGetTasks; +using static Nuke.Common.Tools.VSTest.VSTestTasks; + +[CheckBuildProjectConfigurations] +partial class Build : NukeBuild +{ + /// Support plugins are available for: + /// - JetBrains ReSharper https://nuke.build/resharper + /// - JetBrains Rider https://nuke.build/rider + /// - Microsoft VisualStudio https://nuke.build/visualstudio + /// - Microsoft VSCode https://nuke.build/vscode + + public static int Main () => Execute(x => x.Compile); + + [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] + readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + + [Solution] readonly Solution Solution; + [GitRepository] readonly GitRepository GitRepository; + + AbsolutePath SourceDirectory => RootDirectory / "src"; + AbsolutePath OutputDirectory => RootDirectory / "output"; + + Target Clean => _ => _ + .Before(Restore) + .Executes(() => + { + SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + EnsureCleanDirectory(OutputDirectory); + }); + + + Target InstallDependencies => _ => _ + .Before(Compile) + .Executes(() => + { + Chocolatey("install wixtoolset"); + NpmInstall(x => x + .EnableGlobal() + .AddPackages("dotnettools") + ); + }); + + // logic from 00_Install.bat + Target Restore => _ => _ + .Executes(() => + { + NpmInstall(x => x + .SetProcessWorkingDirectory(SourceDirectory / "NSwag.Npm") + ); + NpmInstall(x => x + .SetProcessWorkingDirectory(SourceDirectory / "NSwag.Integration.TypeScriptWeb") + ); + + MSBuild(x => x + .SetTargetPath(Solution) + .SetTargets("Restore") + .SetMaxCpuCount(Environment.ProcessorCount) + .SetNodeReuse(IsLocalBuild) + .SetVerbosity(MSBuildVerbosity.Minimal) + ); + + DotNetRestore(x => x + .SetProjectFile(Solution) + .SetVerbosity(DotNetVerbosity.Minimal) + ); + + }); + + // logic from 01_Build.bat + Target Compile => _ => _ + .DependsOn(Restore) + .Executes(() => + { + EnsureCleanDirectory(SourceDirectory / "NSwag.Npm" / "bin" / "binaries"); + + Info("Build and copy full .NET command line"); + + MSBuild(x => x + .SetTargetPath(Solution) + .SetTargets("Rebuild") + .SetConfiguration(Configuration) + .SetMaxCpuCount(Environment.ProcessorCount) + .SetNodeReuse(IsLocalBuild) + .SetVerbosity(MSBuildVerbosity.Minimal) + ); + }); + + + // logic from 01_Build.bat + Target Pack => _ => _ + .DependsOn(Compile) + .Executes(() => + { + if (Configuration != Configuration.Release) + { + throw new InvalidOperationException("Cannot pack if compilation hasn't been done in Release mode"); + } + + var npmBinariesDirectory = SourceDirectory / "NSwag.Npm" / "bin" / "binaries"; + + CopyDirectoryRecursively(SourceDirectory / "NSwag.Console" / "bin" / Configuration / "net461", npmBinariesDirectory / "Win"); + + var consoleX86Directory = SourceDirectory / "NSwag.Console.x86" / "bin" / Configuration / "net461"; + CopyFileToDirectory(consoleX86Directory / "NSwag.x86.exe", npmBinariesDirectory / "Win"); + CopyFileToDirectory(consoleX86Directory / "NSwag.x86.exe.config", npmBinariesDirectory / "Win"); + + Info("Publish .NET Core command line done in prebuild event for NSwagStudio.Installer.wixproj"); + + var consoleCoreDirectory = SourceDirectory / "NSwag.ConsoleCore" / "bin" / Configuration; + + CopyDirectoryRecursively(consoleCoreDirectory / "netcoreapp2.1/publish", npmBinariesDirectory / "NetCore21"); + // CopyDirectoryRecursively(consoleCoreDirectory / "netcoreapp2.2/publish", npmBinariesDirectory / "NetCore22"); + // CopyDirectoryRecursively(consoleCoreDirectory / "netcoreapp3.0/publish", npmBinariesDirectory / "NetCore30"); + CopyDirectoryRecursively(consoleCoreDirectory / "netcoreapp3.1/publish", npmBinariesDirectory / "NetCore31"); + + Info("Package nuspecs"); + + NuGetPack(x => x + .SetTargetPath(SourceDirectory / "NSwag.MSBuild" / "NSwag.MSBuild.nuspec") + ); + + NuGetPack(x => x + .SetTargetPath(SourceDirectory / "NSwag.ApiDescription" / "NSwag.ApiDescription.Client.nuspec") + ); + + NuGetPack(x => x + .SetTargetPath(SourceDirectory / "NSwagStudio.Chocolatey" / "NSwagStudio.nuspec") + ); + }); + + + // logic from 02_RunUnitTests.bat + Target UnitTest => _ => _ + .After(Compile) + .Executes(() => + { + var logger = ""; + if (AppVeyor.Instance is not null) + { + logger = "Appveyor"; + } + + VSTest(x => x + .SetLogger(logger) + .SetTestAssemblies(SourceDirectory / "NSwag.Generation.WebApi.Tests" / "bin" / Configuration / "NSwag.Generation.WebApi.Tests.dll") + ); + + /* + VSTest(x => x + .SetLogger(logger) + .SetTestAssemblies(SourceDirectory / "NSwag.Tests" / "bin" / Configuration / "NSwag.Tests.dll") + ); + */ + + // project name + target framework pairs + var dotNetTestTargets = new[] + { + ("NSwag.CodeGeneration.Tests", null), + ("NSwag.CodeGeneration.CSharp.Tests", null), + ("NSwag.CodeGeneration.TypeScript.Tests", null), + ("NSwag.Generation.AspNetCore.Tests", null), + ("NSwag.Core.Tests", null), + ("NSwag.Core.Yaml.Tests", null), + ("NSwag.AssemblyLoader.Tests", "netcoreapp2.1") + }; + + foreach (var (project, targetFramework) in dotNetTestTargets) + { + DotNetTest(x => x + .EnableNoBuild() + .EnableNoRestore() + .SetProjectFile(Solution.GetProject(project)) + .SetFramework(targetFramework) + .SetConfiguration(Configuration) + ); + } + }); + + // logic from 03_RunIntegrationTests.bat + Target IntegrationTest => _ => _ + .After(Compile) + .DependsOn(Samples) + .Executes(() => + { + var nswagCommand = SourceDirectory / "NSwagStudio" / "bin" / Configuration / "nswag.cmd"; + + // project name + runtime pairs + var dotnetTargets = new[] + { + ("NSwag.Sample.NETCore21", "NetCore21"), + ("NSwag.Sample.NETCore31", "NetCore31"), + }; + + foreach (var (projectName, runtime) in dotnetTargets) + { + var project = Solution.GetProject(projectName); + DotNetBuild(x => x + .SetProcessWorkingDirectory(project.Directory) + .SetProperty("CopyLocalLockFileAssemblies", true) + ); + var process = StartProcess(nswagCommand, $"run /runtime:{runtime}", workingDirectory: project.Directory); + process.WaitForExit(); + } + + // project name + runtime pairs + var msbuildTargets = new[] + { + ("NSwag.Sample.NetGlobalAsax", "Winx64"), + ("NSwag.Integration.WebAPI", "Winx64") + }; + + foreach (var (projectName, runtime) in msbuildTargets) + { + var project = Solution.GetProject(projectName); + MSBuild(x => x + .SetProcessWorkingDirectory(project.Directory) + ); + var process = StartProcess(nswagCommand, $"run /runtime:{runtime}", workingDirectory: project.Directory); + process.WaitForExit(); + } + }); + + Target Test => _ => _ + .DependsOn(UnitTest, IntegrationTest); + + // logic from 04_Publish.bat + Target Publish => _ => _ + .After(Compile) + .Executes(() => + { + Npm("publish", SourceDirectory / "NSwag.Npm"); + }); + + // logic from runs.ps1 + Target Samples => _ => _ + .After(Compile) + .Executes(() => + { + var studioProject = Solution.GetProject("NSwagStudio"); + + void NSwagRun( + Project project, + string configurationFile, + string runtime, + string configuration, + bool build) + { + var nswagConfigurationFile = project.Directory / $"{configurationFile}.nswag"; + var nswagSwaggerFile = project.Directory / $"{configurationFile}_swagger.json"; + + DeleteFile(nswagSwaggerFile); + + if (build) + { + DotNetBuild(x => x + .SetProjectFile(project) + .SetConfiguration(configuration) + ); + } + else + { + DotNetRestore(x => x + .SetProjectFile(project) + ); + } + + var cliPath = studioProject.Directory / "bin" / Configuration / runtime / "dotnet-nswag.dll"; + DotNet($"{cliPath} run {nswagConfigurationFile} /variables:configuration=" + configuration); + + if (!File.Exists(nswagSwaggerFile)) + { + throw new Exception($"Output ${nswagSwaggerFile} not generated for {nswagConfigurationFile}."); + } + } + + var samplesPath = RootDirectory / "samples"; + var sampleSolution = ProjectModelTasks.ParseSolution(samplesPath / "Samples.sln"); + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_assembly", "NetCore21", "Release", true); + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_project", "NetCore21", "Release", false); + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_reflection", "NetCore21", "Release", true); + + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_assembly", "NetCore21","Debug", true); + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_project", "NetCore21", "Debug", false); + NSwagRun(sampleSolution.GetProject("Sample.AspNetCore21"), "nswag_reflection", "NetCore21", "Debug", true); + }); + +} diff --git a/build/Configuration.cs b/build/Configuration.cs new file mode 100644 index 0000000000..9c08b1ae9f --- /dev/null +++ b/build/Configuration.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Nuke.Common.Tooling; + +[TypeConverter(typeof(TypeConverter))] +public class Configuration : Enumeration +{ + public static Configuration Debug = new Configuration { Value = nameof(Debug) }; + public static Configuration Release = new Configuration { Value = nameof(Release) }; + + public static implicit operator string(Configuration configuration) + { + return configuration.Value; + } +} diff --git a/build/Directory.Build.props b/build/Directory.Build.props new file mode 100644 index 0000000000..e147d63528 --- /dev/null +++ b/build/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/build/Directory.Build.targets b/build/Directory.Build.targets new file mode 100644 index 0000000000..253260956d --- /dev/null +++ b/build/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/build/_build.csproj b/build/_build.csproj new file mode 100644 index 0000000000..debc04a4ee --- /dev/null +++ b/build/_build.csproj @@ -0,0 +1,22 @@ + + + + Exe + net5.0 + + CS0649;CS0169 + .. + .. + 1 + + + + + + + + + + + + diff --git a/build/nuget.exe b/build/nuget.exe deleted file mode 100644 index d56c57880c..0000000000 Binary files a/build/nuget.exe and /dev/null differ diff --git a/src/NSwag.Integration.TypeScriptWeb/scripts/serviceClientsAngular.ts b/src/NSwag.Integration.TypeScriptWeb/scripts/serviceClientsAngular.ts index ea1c0fb333..f3e6259ac7 100644 --- a/src/NSwag.Integration.TypeScriptWeb/scripts/serviceClientsAngular.ts +++ b/src/NSwag.Integration.TypeScriptWeb/scripts/serviceClientsAngular.ts @@ -36,7 +36,7 @@ export class GeoClient extends MyBaseClass { this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "http://localhost:13452"; } - fromBodyTest(location?: GeoPoint | null | undefined): Observable { + fromBodyTest(location?: GeoPoint | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/FromBodyTest"; url_ = url_.replace(/[?&]$/, ""); @@ -86,7 +86,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - fromUriTest(latitude?: number | undefined, longitude?: number | undefined): Observable { + fromUriTest(latitude?: number | undefined, longitude?: number | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/FromUriTest?"; if (latitude === null) throw new Error("The parameter 'latitude' cannot be null."); @@ -140,7 +140,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - addPolygon(points?: GeoPoint[] | null | undefined): Observable { + addPolygon(points?: GeoPoint[] | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/AddPolygon"; url_ = url_.replace(/[?&]$/, ""); @@ -190,7 +190,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - filter(currentStates?: string[] | null | undefined): Observable { + filter(currentStates?: string[] | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/Filter?"; if (currentStates !== undefined && currentStates !== null) currentStates && currentStates.forEach(item => { url_ += "currentStates=" + encodeURIComponent("" + item) + "&"; }); @@ -238,7 +238,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - reverse(values?: string[] | null | undefined): Observable { + reverse(values?: string[] | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/Reverse?"; if (values !== undefined && values !== null) values && values.forEach(item => { url_ += "values=" + encodeURIComponent("" + item) + "&"; }); @@ -297,7 +297,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - refresh(): Observable { + refresh() : Observable { let url_ = this.baseUrl + "/api/Geo/Refresh"; url_ = url_.replace(/[?&]$/, ""); @@ -343,7 +343,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - uploadFile(file?: FileParameter | null | undefined): Observable { + uploadFile(file?: FileParameter | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/UploadFile"; url_ = url_.replace(/[?&]$/, ""); @@ -398,7 +398,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - uploadFiles(files?: FileParameter[] | null | undefined): Observable { + uploadFiles(files?: FileParameter[] | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/UploadFiles"; url_ = url_.replace(/[?&]$/, ""); @@ -449,7 +449,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - saveItems(request?: GenericRequestOfAddressAndPerson | null | undefined): Observable { + saveItems(request?: GenericRequestOfAddressAndPerson | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/SaveItems"; url_ = url_.replace(/[?&]$/, ""); @@ -507,7 +507,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - getUploadedFile(id: number, override?: boolean | undefined): Observable { + getUploadedFile(id: number, override?: boolean | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/GetUploadedFile/{id}?"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined."); @@ -562,7 +562,7 @@ export class GeoClient extends MyBaseClass { return _observableOf(null); } - postDouble(value?: number | null | undefined): Observable { + postDouble(value?: number | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Geo/PostDouble?"; if (value !== undefined && value !== null) url_ += "value=" + encodeURIComponent("" + value) + "&"; @@ -627,7 +627,7 @@ export class PersonsClient extends MyBaseClass { this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "http://localhost:13452"; } - getAll(): Observable { + getAll() : Observable { let url_ = this.baseUrl + "/api/Persons"; url_ = url_.replace(/[?&]$/, ""); @@ -685,7 +685,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - add(person?: Person | null | undefined): Observable { + add(person?: Person | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Persons"; url_ = url_.replace(/[?&]$/, ""); @@ -735,7 +735,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - find(gender: Gender): Observable { + find(gender: Gender) : Observable { let url_ = this.baseUrl + "/api/Persons/find/{gender}"; if (gender === undefined || gender === null) throw new Error("The parameter 'gender' must be defined."); @@ -796,7 +796,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - findOptional(gender: Gender | null): Observable { + findOptional(gender: Gender | null) : Observable { let url_ = this.baseUrl + "/api/Persons/find2?"; if (gender === undefined) throw new Error("The parameter 'gender' must be defined."); @@ -858,7 +858,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - get(id: string): Observable { + get(id: string) : Observable { let url_ = this.baseUrl + "/api/Persons/{id}"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined."); @@ -919,7 +919,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - delete(id: string): Observable { + delete(id: string) : Observable { let url_ = this.baseUrl + "/api/Persons/{id}"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined."); @@ -968,7 +968,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - transform(person?: Person | null | undefined): Observable { + transform(person?: Person | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Persons/transform"; url_ = url_.replace(/[?&]$/, ""); @@ -1023,7 +1023,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - throw(id: string): Observable { + throw(id: string) : Observable { let url_ = this.baseUrl + "/api/Persons/Throw?"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined and cannot be null."); @@ -1090,7 +1090,7 @@ export class PersonsClient extends MyBaseClass { * @param id The person ID. * @return The person's name. */ - getName(id: string): Observable { + getName(id: string) : Observable { let url_ = this.baseUrl + "/api/Persons/{id}/Name"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined."); @@ -1151,7 +1151,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - addXml(person?: string | null | undefined): Observable { + addXml(person?: string | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Persons/AddXml"; url_ = url_.replace(/[?&]$/, ""); @@ -1205,7 +1205,7 @@ export class PersonsClient extends MyBaseClass { return _observableOf(null); } - upload(data?: Blob | null | undefined): Observable { + upload(data?: Blob | null | undefined) : Observable { let url_ = this.baseUrl + "/api/Persons/upload"; url_ = url_.replace(/[?&]$/, ""); diff --git a/src/NSwag.MSBuild/NSwag.MSBuild.nuspec b/src/NSwag.MSBuild/NSwag.MSBuild.nuspec index 68139beb03..8f5037fe3b 100644 --- a/src/NSwag.MSBuild/NSwag.MSBuild.nuspec +++ b/src/NSwag.MSBuild/NSwag.MSBuild.nuspec @@ -27,8 +27,8 @@ - - - + + + \ No newline at end of file diff --git a/src/NSwag.Sample.NETCore21/swagger_project_cli.json b/src/NSwag.Sample.NETCore21/swagger_project_cli.json index 192de932e4..e9eec060fe 100644 --- a/src/NSwag.Sample.NETCore21/swagger_project_cli.json +++ b/src/NSwag.Sample.NETCore21/swagger_project_cli.json @@ -444,6 +444,7 @@ "definitions": { "SerializableError": { "type": "object", + "description": "Defines a serializable container for storing ModelState information.\nThis information is stored as key/value pairs.", "additionalProperties": {} }, "Pet": { diff --git a/src/NSwag.sln b/src/NSwag.sln index 6bb02119b7..6450bb0310 100644 --- a/src/NSwag.sln +++ b/src/NSwag.sln @@ -160,7 +160,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Generation.AspNetCore EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "00 Build", "00 Build", "{6F5E4FDF-0A82-42D5-94AC-A9CD43CC931D}" ProjectSection(SolutionItems) = preProject - ..\build\01_Build.bat = ..\build\01_Build.bat ..\azure-pipelines.yml = ..\azure-pipelines.yml EndProjectSection EndProject @@ -168,6 +167,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Sample.NETCore20", "N EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Sample.NETCore20.Part", "NSwag.Sample.NETCore20.Part\NSwag.Sample.NETCore20.Part.csproj", "{60438B13-8111-44C3-B5E5-34C3F30FF234}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{AC3D8125-AE21-49FC-A217-D96C7B585FF9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -181,6 +182,8 @@ Global ReleaseTypeScriptStrict|x86 = ReleaseTypeScriptStrict|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC3D8125-AE21-49FC-A217-D96C7B585FF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC3D8125-AE21-49FC-A217-D96C7B585FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {75B3F91D-687E-4FB3-AD45-CCFA3C406DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75B3F91D-687E-4FB3-AD45-CCFA3C406DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {75B3F91D-687E-4FB3-AD45-CCFA3C406DB4}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1096,6 +1099,7 @@ Global {2333521E-F537-4F4F-98B9-7CD22B20A5D7} = {634E4ABD-29EC-4EB2-81EF-7E41D6D6F6E0} {37563171-4C09-4BCD-B2FB-08F786198909} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5} {60438B13-8111-44C3-B5E5-34C3F30FF234} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5} + {AC3D8125-AE21-49FC-A217-D96C7B585FF9} = {6F5E4FDF-0A82-42D5-94AC-A9CD43CC931D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {98FCEEE2-A45C-41E7-B2ED-1B14755E9067}