Compare commits

...

36 Commits

Author SHA1 Message Date
77fe2e4d48 💄 Aligns styling rules 2026-03-20 14:20:37 +02:00
75563e9ff8 📝 Updates README with enhanced getting started instructions and feature details 2026-03-20 14:04:33 +02:00
eea714e68b 🙈 Updates ignore files 2026-03-20 13:48:26 +02:00
dde7dbe5c4 Ensures all template dependencies will have a version, overridable at Directory.Packages.props 2026-03-20 13:23:17 +02:00
b9912b95eb 💡 Adds compose sample for referecne 2026-03-20 12:59:18 +02:00
d3454f5327 📝 Lints OpenAPI documents for issues 2026-03-20 12:53:56 +02:00
edff3179c9 🔧 Streamlines inconsistencies and resolves current issues 2026-03-20 12:06:27 +02:00
6576acc2e5 🔧 Fixes generated code detection and xml formatting 2026-03-20 03:17:39 +02:00
d2f03a06b8 🔧 Updates Directory.Build.props to modern standards 2026-03-20 03:12:25 +02:00
17743ea8b3 🔧 Adds companion nugets when appropriate and handles OpenAPI file naming 2026-03-20 03:11:59 +02:00
6844525107 🔧 Updates .gitattributes to include merge strategy for Directory.Packages.template.props - Upstream always wins 2026-03-20 03:10:00 +02:00
223b9013be 🔧 Updates unit testing settings for Rider 2026-03-20 03:03:16 +02:00
e70bed02a2 🔧 Adds ready to run option 2026-03-20 03:02:14 +02:00
dab6f2faab 🔧 Customizes workspace settings to handle common files 2026-03-20 03:01:43 +02:00
58d562919e 🔧 Moves template installed nugets to their own file 2026-03-20 02:49:00 +02:00
d554c2bb35 🔧 Formats whitespace 2026-03-20 02:47:59 +02:00
f0cd953e80 📝 Disables documentation warnings for samples and tests 2026-03-20 02:46:09 +02:00
fe4e5ebef1 🔧 Migrates test settings to modern Testing Platform 2026-03-20 01:57:46 +02:00
cbf8083d31 🔧 Adds .gitattributes to configure merge strategy for Directory.Packages.props 2026-03-20 01:55:34 +02:00
dda4b3048b 🙈 Updates .gitignore to refine artifact handling and exclude unnecessary files 2026-03-20 01:55:26 +02:00
52aa34a315 📦 Moves nugets and published files under the Artifacts path 2026-03-20 01:55:02 +02:00
14e6cd7087 🔧 Removes all tasks from tasks.json
These were added long before GitVersion was embedded to auto version projects
2026-03-20 01:53:35 +02:00
12dd15e088 🔧 Fixes formatting for PublishDir in Directory.Build.props 2026-03-17 02:51:16 +02:00
6799fb6d17 🔧 Adds nuget.config for package source configuration
Currently, package source mapping falls back to nuget, private feed only contains pre-release versions of libraries
2026-03-17 01:58:37 +02:00
617b788159 🔧 Decouples docker layer cache from .git folder 2026-03-17 01:55:59 +02:00
c02cd0f662 🔧 Updates Directory.Build.props with modern standards
- Treats warnings as errors unless otherwise configured
- Enables transitive library version pinning
- Uses proper deterministic paths on artifacts layout
- Removes deprecated properties
2026-03-16 19:03:53 +02:00
cc7e434348 🔧 Updates global.json to include SDK configuration for prerelease support 2026-03-16 19:01:16 +02:00
153ecc80df 🔥 Removes Deterministic Build Check sample program 2026-03-16 18:59:57 +02:00
bd95a4717c 🔖 Sanitizes changelog generation 2026-03-16 18:56:24 +02:00
4f79085cd9 🔧 Adds Deterministic Build and Source Paths Check functionality 2026-03-16 18:33:41 +02:00
d27fb7bbca 🧪Migrates to MTP testing mode as per .NET 10 guidelines 2026-03-02 16:55:10 +02:00
6a6b021778 🔧 Moves CheckForOverflowUnderflow property to Debug only configurations
Contains measurable overhead that should be redundant with proper unit tests and handling
2026-03-02 16:08:15 +02:00
b59e0d12ab 🔧 Adds guards for specific properties & targets instead of applying them on all projects 2026-03-02 16:00:30 +02:00
ce0ba9b3b2 🔧 Sets internal visibility on props only 2026-03-02 15:53:46 +02:00
08d94e2221 🔧 Migrates code analysis configuration to .globalconfig as per current recommendations 2026-03-02 15:49:01 +02:00
a0eb6a7579 🔧 Adds Dockerfile for generic multi-stage build and deployment setup 2026-03-02 15:38:52 +02:00
29 changed files with 600 additions and 445 deletions

View File

@@ -1,6 +1,4 @@
**/.dockerignore
**/.env
**/.gitignore
**/.project
**/.settings
**/.toolstarget
@@ -13,10 +11,22 @@
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/docs
**/docker
**/artifacts
**/.idea
**/.github
**/logs
**/Logs
**/.*ignore
.gitattributes
**/.env
compose*.yaml

View File

@@ -10,5 +10,8 @@ trim_trailing_whitespace = true
[*.sh]
end_of_line = lf
[{*.Generated.*.cs,Migrations/*,Scripts/*}]
[*.{xml,csproj,props,targets,nuspec}]
max_line_length = 1000
[{*.Generated.*.cs,**/Migrations/*.cs}]
generated_code = true

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
Directory.Packages.props merge=union
Directory.Packages.template.props merge=upstream-wins

14
.gitignore vendored
View File

@@ -1139,7 +1139,6 @@ TestResults
# Visual Studio Code
!*.code-workspace
!.vscode/tasks-*.json
## Web Assets managed by libman
**/wwwroot/dist/*
@@ -1150,11 +1149,10 @@ TestResults
*.coverage.xml
## Public Assets
packages/*
upload/*
!upload/.gitkeep
!packages/.gitkeep
!artifacts/
artifacts/*
!artifacts/.gitkeep
**/OpenApi/*.json
artifacts/**
!artifacts/**/
!artifacts/**/.gitkeep
## Machine-specific overrides
compose.override.yaml

View File

@@ -1,8 +1,24 @@
is_global = true
# Ruleset replacement
dotnet_diagnostic.CA2007.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2007
# C# compiler diagnostics
dotnet_diagnostic.CS1573.severity = none # Missing XML comment for parameter
dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS2008.severity = none # No source files specified
# Code quality analyzers
dotnet_diagnostic.CA1303.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1303
dotnet_diagnostic.CA1707.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1707
dotnet_diagnostic.CA1848.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1848
dotnet_diagnostic.CA2007.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2007
# Serilog analyzers
dotnet_diagnostic.Serilog004.severity = none # Serilog context property accessor
# StyleCop analyzers
dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this
dotnet_diagnostic.SA1633.severity = none # File must have header
dotnet_diagnostic.SA1649.severity = none # File name must match first type name
# Organize usings
dotnet_sort_system_directives_first = true

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
<ResultsDirectory>.\TestResults</ResultsDirectory>
<DisableAppDomain>true</DisableAppDomain>
</RunConfiguration>
<xUnit>
<Culture>invariant</Culture>
<LongRunningTestSeconds>5</LongRunningTestSeconds>
</xUnit>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage">
<Configuration>
<Format>cobertura</Format>
<CoverageFileName>coverage.xml</CoverageFileName>
<ExcludeByAttribute>ObsoleteAttribute,GeneratedCodeAttribute,CompilerGeneratedAttribute</ExcludeByAttribute>
<ExcludeByFile>**/tests/**.cs,**/samples/**.cs</ExcludeByFile>
<SingleHit>false</SingleHit>
<UseSourceLink>true</UseSourceLink>
<IncludeTestAssembly>false</IncludeTestAssembly>
<SkipAutoProps>true</SkipAutoProps>
<DeterministicReport>true</DeterministicReport>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>

2
.spectral.yaml Normal file
View File

@@ -0,0 +1,2 @@
extends:
- spectral:oas

186
.vscode/tasks.json vendored
View File

@@ -1,188 +1,4 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "restore",
"command": "dotnet",
"type": "shell",
"args": [
"restore",
"--verbosity",
"minimal",
"-bl:logs\\restore.binlog"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "never",
"panel": "shared"
},
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--configuration",
"Release",
"--no-restore",
"--nologo",
"--verbosity",
"minimal",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"/p:TF_BUILD=true",
"-bl:logs\\build.binlog"
],
"dependsOrder": "sequence",
"dependsOn": [
"restore"
],
"group": "build",
"presentation": {
"reveal": "silent",
"panel": "shared",
"clear": true
},
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",
"type": "shell",
"args": [
"test",
"--configuration",
"Release",
"--no-build",
"--verbosity",
"normal",
"--collect:'Code Coverage'",
"-bl:logs\\test.binlog"
],
"group": {
"kind": "test",
"isDefault": true
},
"dependsOrder": "sequence",
"dependsOn": [
"build"
],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
"problemMatcher": "$msCompile"
},
{
"label": "pack",
"command": "dotnet",
"type": "shell",
"args": [
"pack",
"--configuration",
"Release",
"--no-restore",
"--nologo",
"--verbosity",
"minimal",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"/p:TF_BUILD=true",
"-bl:logs\\pack.binlog"
],
"dependsOrder": "sequence",
"dependsOn": [
"publish"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "shell",
"args": [
"publish",
"--configuration",
"Release",
"--no-restore",
"--no-build",
"--nologo",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"/p:TF_BUILD=true",
"-bl:logs\\publish.binlog"
],
"dependsOrder": "sequence",
"dependsOn": [
"test"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "never",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": true
},
"problemMatcher": "$msCompile"
},
{
"label": "clean",
"command": "dotnet",
"type": "shell",
"args": [
"clean",
"--configuration",
"Release",
"--verbosity",
"minimal"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "never",
"panel": "shared"
},
"problemMatcher": "$msCompile"
},
{
"label": "cleanPublished",
"type": "process",
"command": "del",
"args": [
"-rf",
"upload/*"
],
"group": "build",
"presentation": {},
"problemMatcher": "$msCompile"
}
]
"tasks": []
}

View File

@@ -6,7 +6,6 @@
</ImportGroup>
<PropertyGroup Label="Project">
<NuGetAudit>true</NuGetAudit>
<!-- Suppresses display of the sign-on banner -->
<NoLogo>true</NoLogo>
<!-- Prepend organization name to default namespace -->
@@ -16,7 +15,6 @@
<!-- Generate documentation file and ignore warnings for undocumented elements -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishDocumentationFile>true</PublishDocumentationFile>
<NoWarn>$(NoWarn);1591;1701;1702;NU1507</NoWarn>
<!-- Error out upon trying to package projects marked as not packable -->
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<!-- Disable warnings on preview SDK versions -->
@@ -33,37 +31,46 @@
<AnalysisLevel>preview</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<CodeAnalysisTreatWarningsAsErrors Condition="$(CodeAnalysisTreatWarningsAsErrors) == ''">false</CodeAnalysisTreatWarningsAsErrors>
<!-- <TreatWarningsAsErrors Condition="'$(TreatWarningsAsErrors)' ==
''">true</TreatWarningsAsErrors> -->
<CodeAnalysisTreatWarningsAsErrors>$(TreatWarningsAsErrors)</CodeAnalysisTreatWarningsAsErrors>
<!-- Locked mode should only be enabled on CI -->
<RestoreLockedMode Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</RestoreLockedMode>
<!-- Use deterministic builds -->
<Deterministic Condition="'$(ContinuousIntegrationBuild)' == 'True'">true</Deterministic>
<DeterministicSourcePaths Condition="'$(IsTestProject)' == 'true'">false</DeterministicSourcePaths>
<DeterministicSourcePaths>true</DeterministicSourcePaths>
<PathMap Condition="$(ArtifactsPath) != ''">$(ArtifactsPath)=/_/generated</PathMap>
<!-- Use latest language version supported by the SDK -->
<LangVersion>preview</LangVersion>
<ClsCompliant>false</ClsCompliant>
<!-- Defaults to checked context for math operations, throws OverflowException when needed -->
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<ApplicationVisualStyles>true</ApplicationVisualStyles>
<EmitCompilerGeneratedFiles>false</EmitCompilerGeneratedFiles>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<GenerateRequiresPreviewFeaturesAttribute>False</GenerateRequiresPreviewFeaturesAttribute>
<DefaultItemExcludes>$(DefaultItemExcludes);*.log;*.binlog</DefaultItemExcludes>
</PropertyGroup>
<PropertyGroup>
<!-- Defaults to checked context for math operations, throws OverflowException when needed -->
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition="$(ArtifactsPath) == '' AND $(DisableCustomArtifactsPath) != 'true'">
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
</PropertyGroup>
<PropertyGroup Condition="$(ArtifactsPath) != ''">
<UseArtifactsOutput>true</UseArtifactsOutput>
<PackageOutputPath>$(ArtifactsPath)/nuget</PackageOutputPath>
<PublishDir>$(ArtifactsPath)/upload/$(MSBuildProjectName)</PublishDir>
</PropertyGroup>
<PropertyGroup Label="Central Package Management">
<NoWarn>$(NoWarn);NU1506</NoWarn>
<!-- Create Directory.Packages.props file next to solution to use -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- Any transitive dependency defined below will be used explicitly on the version stated -->
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<RestoreIgnoreFailedSources>true</RestoreIgnoreFailedSources>
<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>
</PropertyGroup>
<PropertyGroup Label="Project Assets">
@@ -84,22 +91,28 @@
<ProjectReadMeExists Condition="$(ProjectReadMe) != ''">true</ProjectReadMeExists>
</PropertyGroup>
<PropertyGroup Label="SDK Functionality"
Condition="'$(UsingMicrosoftNETSdkWeb)' == 'True' OR '$(UsingMicrosoftNETSdkWorker)' == 'True'">
<PropertyGroup Label="SDK Functionality" Condition="'$(UsingMicrosoftNETSdkWeb)' == 'True' OR '$(UsingMicrosoftNETSdkWorker)' == 'True'">
<IsPackable>false</IsPackable>
<PackAsTool>false</PackAsTool>
</PropertyGroup>
<PropertyGroup Label="Disable Launch Settings for Worker Service"
Condition="'$(UsingMicrosoftNETSdkWorker)' == 'True'">
<PropertyGroup Label="Test Functionality" Condition="$(IsTestProject) == 'true'">
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
</PropertyGroup>
<PropertyGroup Label="Disable Launch Settings for Worker Service" Condition="'$(UsingMicrosoftNETSdkWorker)' == 'True'">
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
</PropertyGroup>
<PropertyGroup Label="OpenAPI Analyzers" Condition="'$(UsingMicrosoftNETSdkWeb)' == 'True'">
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)/OpenApi</OpenApiDocumentsDirectory>
<OpenApiDocumentsDirectory>$(ArtifactsPath)/OpenApi</OpenApiDocumentsDirectory>
</PropertyGroup>
<PropertyGroup Label="MAUI Features">
<PropertyGroup Condition="$(UseWindowsForms) == 'true'">
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<ApplicationVisualStyles>true</ApplicationVisualStyles>
</PropertyGroup>
<PropertyGroup Label="MAUI Features" Condition="$(UseMaui) == 'true'">
<TrimMode>Full</TrimMode>
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>
@@ -107,24 +120,6 @@
<PropertyGroup Label="Entity Framework Core - Compiled Model generation">
<EFOptimizeContext>true</EFOptimizeContext>
<EFScaffoldModelStage>publish</EFScaffoldModelStage>
</PropertyGroup>
<!-- Sets deterministic source paths for CI builds -->
<PropertyGroup Label="CI Build">
<!--
https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#system-variables -->
<ContinuousIntegrationBuild Condition="'$(TF_BUILD)' == 'true'">true</ContinuousIntegrationBuild>
<!--
https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables -->
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
<!-- https://docs.gitlab.com/ee/ci/variables/predefined_variables.html -->
<ContinuousIntegrationBuild Condition="'$(GITLAB_CI)' == 'true'">true</ContinuousIntegrationBuild>
<!-- https://www.appveyor.com/docs/environment-variables/ -->
<ContinuousIntegrationBuild Condition="'$(APPVEYOR)' == 'True'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<!-- Mark assemblies as CLS compliant so the compiler warns on non compliant usage -->
@@ -139,12 +134,6 @@
</AssemblyAttribute>
</ItemGroup>
<!-- Use .NET ruleset for analyzers -->
<PropertyGroup Label="Code Analysis Ruleset"
Condition="Exists('$(MSBuildThisFileDirectory)\dotnet.ruleset')">
<CodeAnalysisRuleset>$(MSBuildThisFileDirectory)\dotnet.ruleset</CodeAnalysisRuleset>
</PropertyGroup>
<!-- Use readme as nuget information -->
<ItemGroup Label="Readme" Condition="$(IsPackable) == 'True' AND $(ProjectReadMeExists) == 'True'">
<Content Include="$(ProjectReadMe)" PackagePath="README.md" />
@@ -155,14 +144,12 @@
<Content Include="$(ProjectIcon)" Pack="true" PackagePath="icon.png" Visible="false" />
</ItemGroup>
<!-- Uses pretty xUnit configuration -->
<ItemGroup Label="xUnit Configuration"
Condition="Exists('$(MSBuildThisFileDirectory)\xunit.runner.json') AND $(IsTestProject)=='True'">
<Content Include="$(MSBuildThisFileDirectory)\xunit.runner.json" Link="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" Visible="false" />
<ItemGroup Label="Test Configuration" Condition="Exists('$(MSBuildThisFileDirectory)\testconfig.json') AND $(IsTestProject)=='True'">
<Content Include="$(MSBuildThisFileDirectory)\testconfig.json" Link="testconfig.json"
CopyToOutputDirectory="PreserveNewest" Visible="false" />
</ItemGroup>
<ItemGroup Label="Stylecop Configuration"
Condition="Exists('$(MSBuildThisFileDirectory)\stylecop.json')">
<ItemGroup Label="Stylecop Configuration" Condition="Exists('$(MSBuildThisFileDirectory)\stylecop.json')">
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\stylecop.json" Link="stylecop.json"
CopyToOutputDirectory="Never" Visible="false" />
</ItemGroup>
@@ -180,14 +167,9 @@
<Authors>Alexandros Kritikos</Authors>
<CurrentYear>$([System.DateTime]::Now.ToString(yyyy))</CurrentYear>
<Copyright>Copyright © 2017-$(CurrentYear) Kritikos IO. All rights reserved.</Copyright>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageOutputPath Condition="$(PackageOutputPath) == ''">$(MSBuildThisFileDirectory)/packages</PackageOutputPath>
<PublishDir Condition="$(PublishDir) == ''">
$(MSBuildThisFileDirectory)/upload/$(MSBuildProjectName)</PublishDir>
<PackageIcon Condition="$(ProjectIconExists) == 'True'">icon.png</PackageIcon>
<PackageReadmeFile Condition="$(ProjectReadMeExists) == 'True'">README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryType>git</RepositoryType>
<!-- Optional: Embed source files that are not tracked by the source control manager to the PDB -->
<!-- This is useful if you generate files during the build -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -200,8 +182,7 @@
</PropertyGroup>
<ItemGroup Label="Versioning" Condition="'$(IsTestProject)' != 'true'">
<PackageReference Include="GitVersion.MsBuild"
Condition="Exists('$(MSBuildThisFileDirectory)\.git')">
<PackageReference Include="GitVersion.MsBuild" Condition="Exists('$(MSBuildThisFileDirectory)\.git')">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -214,14 +195,6 @@
</PackageReference>
</ItemGroup>
<ItemGroup Label="Test Frameworks" Condition="'$(IsTestProject)' == 'true'">
<PackageReference Include="NSubstitute"/>
<PackageReference Include="NSubstitute.Analyzers.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Label="SourceLink" Condition="$(IsPackable) == 'true'">
<SourceLinkGiteaHost Include="git.kritikos.io" />
<PackageReference Include="Microsoft.Sbom.Targets" PrivateAssets="All" />

View File

@@ -1,24 +1,38 @@
<Project>
<Target Name="RestoreNpmAssets">
<Target Name="RestoreNpmAssets" Condition="Exists('$(MSBuildProjectDirectory)/package.json')" BeforeTargets="Restore">
<Exec Command="npm install" />
</Target>
<PropertyGroup Label="Test Functionality" Condition="$(IsTestProject) == 'true'">
<IsPackable>false</IsPackable>
<PackAsTool>false</PackAsTool>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<RunSettingsFilePath>$(MSBuildThisFileDirectory)\.runsettings</RunSettingsFilePath>
<NoWarn>$(NoWarn);CA1707</NoWarn>
</PropertyGroup>
<ItemGroup Label="Hide Assets">
<None Update="icon.png" Visible="false" />
<None Update="..\..\README.md" Visible="false" />
<None Update="packages.lock.json" Visible="false" />
</ItemGroup>
<ItemGroup
Condition="@(PackageReference->WithMetadataValue('Identity','Microsoft.AspNetCore.OpenApi')->Count()) &gt; 0">
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup
Condition="@(PackageReference->WithMetadataValue('Identity','NSubstitute')->Count()) &gt; 0">
<PackageReference Include="NSubstitute.Analyzers.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<Target Name="RenameOpenApiDocuments"
AfterTargets="GenerateOpenApiDocuments"
Condition="'$(OpenApiDocumentsDirectory)' != ''">
<Move SourceFiles="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName).json"
DestinationFiles="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName)-v1.json"
Condition="Exists('$(OpenApiDocumentsDirectory)/$(MSBuildProjectName).json')" />
</Target>
<Target Name="AddInternalsVisibleTo" BeforeTargets="BeforeCompile">
<!--
Add an ItemGroup with tags in the following format:
@@ -36,11 +50,6 @@
-> [assembly: InternalsVisibleTo("ClassLibrary1.FunctionalTests")]
-->
<!-- Add default suffix for tests -->
<ItemGroup>
<InternalsVisibleToSuffix Include=".Tests" />
</ItemGroup>
<!-- Handle InternalsVisibleTo -->
<ItemGroup Condition="'@(InternalsVisibleTo->Count())' &gt; 0">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
@@ -58,8 +67,9 @@
</Target>
<Target Name="CreateReleaseNotesFromFile" BeforeTargets="GenerateNuspec"
Condition="Exists('$(MSBuildThisFileDirectory)changes.log')">
<ReadLinesFromFile File="$(MSBuildThisFileDirectory)changes.log">
DependsOnTargets="GenerateChangelogFromGit"
Condition="Exists('$(ChangeLogPath)')">
<ReadLinesFromFile File="$(ChangeLogPath)">
<Output TaskParameter="Lines" ItemName="ReleaseNoteLines" />
</ReadLinesFromFile>
<PropertyGroup>
@@ -67,6 +77,96 @@
</PropertyGroup>
</Target>
<PropertyGroup>
<ChangeLogPath Condition="'$(ChangeLogPath)' == ''">$(BaseIntermediateOutputPath)changes.log</ChangeLogPath>
<ChangeLogFromRef Condition="'$(ChangeLogFromRef)' == ''"></ChangeLogFromRef>
<ChangeLogCommitLimit Condition="'$(ChangeLogCommitLimit)' == ''">100</ChangeLogCommitLimit>
<ChangeLogUpstreamMergePattern Condition="'$(ChangeLogUpstreamMergePattern)' == ''">from .*/</ChangeLogUpstreamMergePattern>
<!-- Set to false to exclude commits from ProjectReference directories in the changelog -->
<ChangeLogIncludeReferences Condition="'$(ChangeLogIncludeReferences)' == ''">true</ChangeLogIncludeReferences>
</PropertyGroup>
<Target Name="GenerateChangelogFromGit"
BeforeTargets="GenerateNuspec"
Condition="'$(GenerateChangelogFromGit)' != 'false' AND Exists('$(MSBuildThisFileDirectory).git')">
<!-- #1: nearest reachable tag from HEAD, not globally newest; capture exit code to guard against no-tag repos -->
<Exec Command="git describe --tags --abbrev=0"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true"
IgnoreExitCode="true"
StandardErrorImportance="low">
<Output TaskParameter="ConsoleOutput" PropertyName="GitLastTagRaw" />
<Output TaskParameter="ExitCode" PropertyName="GitDescribeExitCode" />
</Exec>
<Exec Command="git rev-list --first-parent --merges -n 1 HEAD"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitLastMergeRaw" />
</Exec>
<!-- #4: configurable upstream merge pattern via ChangeLogUpstreamMergePattern -->
<Exec Command="git rev-list --first-parent --merges -n 1 --grep=&quot;$(ChangeLogUpstreamMergePattern)&quot; HEAD"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitLastUpstreamMergeRaw" />
</Exec>
<PropertyGroup>
<!-- only accept the tag when git describe actually succeeded -->
<GitLastTag Condition="'$(GitDescribeExitCode)' == '0'">$([System.String]::Copy('$(GitLastTagRaw)').Trim())</GitLastTag>
<GitLastUpstreamMergeCommit>$([System.String]::Copy('$(GitLastUpstreamMergeRaw)').Trim())</GitLastUpstreamMergeCommit>
<GitLastMergeCommit>$([System.String]::Copy('$(GitLastMergeRaw)').Trim())</GitLastMergeCommit>
<ChangeLogRange Condition="'$(ChangeLogFromRef)' != ''">$(ChangeLogFromRef)..HEAD</ChangeLogRange>
<ChangeLogRange Condition="'$(ChangeLogRange)' == '' AND '$(GitLastTag)' != ''">$(GitLastTag)..HEAD</ChangeLogRange>
<ChangeLogRange Condition="'$(ChangeLogRange)' == '' AND '$(GitLastUpstreamMergeCommit)' != ''">$(GitLastUpstreamMergeCommit)..HEAD</ChangeLogRange>
<ChangeLogRange Condition="'$(ChangeLogRange)' == '' AND '$(GitLastMergeCommit)' != ''">$(GitLastMergeCommit)..HEAD</ChangeLogRange>
<!-- #3: subject-only; space before %s would split the arg on Windows cmd, so bullet is added by MSBuild transform below -->
<GitPrettyFormat Condition="$([MSBuild]::IsOSPlatform('Windows')) == 'true'">--pretty=format:%25%25s</GitPrettyFormat>
<GitPrettyFormat Condition="$([MSBuild]::IsOSPlatform('Windows')) != 'true'">--pretty=format:%25s</GitPrettyFormat>
<GitLogBaseCommand>git --no-pager log --no-merges $(GitPrettyFormat)</GitLogBaseCommand>
</PropertyGroup>
<!-- Collect directories for path scoping; optionally include ProjectReference paths -->
<ItemGroup>
<ChangeLogPaths Include="&quot;$(MSBuildProjectDirectory)&quot;" />
<ChangeLogPaths Condition="'$(ChangeLogIncludeReferences)' == 'true'"
Include="@(ProjectReference->'&quot;%(RootDir)%(Directory).&quot;')" />
</ItemGroup>
<PropertyGroup>
<ChangeLogPathSpec>@(ChangeLogPaths, ' ')</ChangeLogPathSpec>
</PropertyGroup>
<Exec Condition="'$(ChangeLogRange)' != ''"
Command="$(GitLogBaseCommand) $(ChangeLogRange) -- $(ChangeLogPathSpec)"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true">
<Output TaskParameter="ConsoleOutput" ItemName="ChangeLogLines" />
</Exec>
<Exec Condition="'$(ChangeLogRange)' == ''"
Command="$(GitLogBaseCommand) --max-count=$(ChangeLogCommitLimit) -- $(ChangeLogPathSpec)"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true">
<Output TaskParameter="ConsoleOutput" ItemName="ChangeLogLines" />
</Exec>
<!-- #3: prepend bullet here, safely, with no shell involved; skip writing an empty file -->
<WriteLinesToFile Condition="@(ChangeLogLines->Count()) &gt; 0"
File="$(ChangeLogPath)"
Lines="@(ChangeLogLines->'- %(Identity)')"
Overwrite="true"
WriteOnlyWhenDifferent="true"
Encoding="UTF-8" />
<Message Importance="High" Condition="'$(ChangeLogRange)' != ''"
Text="Generated changelog at $(ChangeLogPath) using range $(ChangeLogRange)." />
<Message Importance="High" Condition="'$(ChangeLogRange)' == ''"
Text="Generated changelog at $(ChangeLogPath) using last $(ChangeLogCommitLimit) commits." />
</Target>
<Import
Condition="Exists('$(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).EntityFrameworkCore.targets')"
Project="$(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).EntityFrameworkCore.targets" />

View File

@@ -1,13 +1,6 @@
<Project>
<ItemGroup Label="Packages">
<PackageVersion Include="GitVersion.MsBuild" Version="6.5.1" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.Sbom.Targets" Version="4.1.5" />
<PackageVersion Include="NewStyleCop.Analyzers" Version="1.2.1" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
</ItemGroup>
<Import Project="Directory.Packages.template.props" />
</Project>

View File

@@ -0,0 +1,9 @@
<Project>
<ItemGroup Label="Packages">
<PackageVersion Include="GitVersion.MsBuild" Version="6.6.2" />
<PackageVersion Include="Microsoft.Sbom.Targets" Version="4.1.5" />
<PackageVersion Include="NewStyleCop.Analyzers" Version="1.2.1" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.5" />
</ItemGroup>
</Project>

View File

@@ -1,53 +0,0 @@
<Project>
<Target Name="GetGitBranch" BeforeTargets="ValidateSolutionConfiguration"
Condition="!Exists('$(SolutionDir)changes.log')">
<Exec ConsoleToMSBuild="True" IgnoreExitCode="true" Command="git rev-parse --abbrev-ref HEAD"
StandardOutputImportance="low" StandardErrorImportance="low" ContinueOnError="WarnAndContinue">
<Output TaskParameter="ConsoleOutput" PropertyName="GitBranch" />
<Output TaskParameter="ExitCode" PropertyName="GitBranchExitCode" />
</Exec>
</Target>
<Target Name="GetGitTag" AfterTargets="GetGitBranch"
Condition="$(GitBranch) != '' AND !Exists('$(SolutionDir)changes.log')">
<Exec ConsoleToMSBuild="True" IgnoreExitCode="true"
Condition="$(GitBranch) == 'master' OR $(GitBranch) == 'main'"
Command='git describe --tags --abbrev=0 --exclude "v*-*"' StandardOutputImportance="low"
StandardErrorImportance="low" ContinueOnError="WarnAndContinue">
<Output TaskParameter="ConsoleOutput" PropertyName="GitTag" />
<Output TaskParameter="ConsoleOutput" PropertyName="GitVerTag" />
<Output TaskParameter="ExitCode" PropertyName="GitTagExitCode" />
</Exec>
<Exec ConsoleToMSBuild="True" IgnoreExitCode="true"
Condition="$(GitBranch) != 'master' AND $(GitBranch) != 'main'"
Command="git describe --tags --abbrev=0" StandardOutputImportance="low"
StandardErrorImportance="low" ContinueOnError="WarnAndContinue">
<Output TaskParameter="ConsoleOutput" PropertyName="GitTag" />
<Output TaskParameter="ExitCode" PropertyName="GitTagExitCode" />
</Exec>
</Target>
<Target Name="CreateGitLog" AfterTargets="GetGitTag"
Condition="!Exists('$(MSBuildThisFileDirectory)\changes.log')">
<PropertyGroup>
<GitBaseCommand>git log --no-merges</GitBaseCommand>
<GitCommand Condition="$([MSBuild]::IsOSPlatform('Windows')) == 'true'">$(GitBaseCommand)
--pretty=format:&quot;- %25%25s&quot;</GitCommand>
<GitCommand Condition="$([MSBuild]::IsOSPlatform('Windows')) != 'true'">$(GitBaseCommand)
--pretty=format:&quot;- %25s&quot;</GitCommand>
</PropertyGroup>
<Exec Condition="$(GitTagExitCode) == 0"
Command='$(GitCommand) $(GitTag)..HEAD > &quot;$(MSBuildThisFileDirectory)changes.log&quot;'
StandardOutputImportance="low" StandardErrorImportance="low" ContinueOnError="WarnAndContinue" />
<Exec Condition="$(GitTagExitCode) != 0"
Command="$(GitCommand) > &quot;$(MSBuildThisFileDirectory)changes.log&quot;"
StandardOutputImportance="low" StandardErrorImportance="low" ContinueOnError="WarnAndContinue" />
<Exec Condition="$([MSBuild]::IsOSPlatform('Windows')) == 'true'"
Command="type $(MSBuildThisFileDirectory)changes.log | findstr -v 💚 | findstr -v : | findstr -v 🔧 > $(MSBuildThisFileDirectory)changes.log"
StandardOutputImportance="low" StandardErrorImportance="low" ContinueOnError="WarnAndContinue" />
<Exec Condition="$([MSBuild]::IsOSPlatform('Windows')) != 'true'"
Command="sed -i '/^- 💚/ d' $(MSBuildThisFileDirectory)changes.log &amp;&amp; sed -i '/^- 🔧/ d' $(MSBuildThisFileDirectory)changes.log &amp;&amp; sed -i '/:/d' $(MSBuildThisFileDirectory)changes.log"
StandardOutputImportance="low" StandardErrorImportance="low" ContinueOnError="WarnAndContinue" />
</Target>
</Project>

View File

@@ -2,29 +2,55 @@
A starting point for new .NET projects, based on opinionated rules.
1. In order to be able to update your repository with the latest changes, you can use the following command **after creating** your repo:
## Getting started
1. In order to be able to update your repository with the latest changes, you can use the following command **after creating** your repo:
```bash
git remote add template https://github.com/kritikos-io/templates-dotnet
git fetch --all
git merge template/main --allow-unrelated-histories
```
2. Do this as soon as possible, as the unrelated histories flag will lead to a few conflicts that you will need to resolve manually.
3. Afterwards, you can pull future changes using
1. Do this as soon as possible, as the unrelated histories flag will lead to a few conflicts that you will need to resolve manually.
1. Afterwards, you can pull future changes using
```bash
git pull template main
```
1. Rename the solution and project files, replacing 'Solution' to match your project name.
1. Solution.slnx
1. Solution.sln.DotSettings
1. Solution.code-workspace
4. Rename the solution and project files, replacing 'Solution' to match your project name.
1. Solution.sln
2. Solution.sln.DotSettings
3. Solution.code-workspace
## Features
> Keep in mind that until the dotnet toolset handles generating new projects correctly, you will need to edit new csproj files and remove Version attributes from PackageReference entries. For more details consult [Central Package Management].
Apart from a robust configuration, this template specifically includes:
- Git history to semantic versioning integration using GitVersion
- Artifacts layout, to avoid bin/obj folders all over the place. These are placed in the `artifacts` folder at the solution level, and are further organized by project and configuration. Can also be overridden either at the project level or by a Directory.Build.props above the repository root to redirect them to a different location.
- Central package management, to avoid version conflicts and make it easier to update dependencies.
- Additional targets that enable change log generated from git history, and SBOM generation.
## Docker
A multi-stage Dockerfile is provided in `docker/` with multiple targets for different use cases. Sample usage is provided in [`compose.sample.yaml`](./docker/compose.sample.yaml).
The `RUNTIME_BASE` build arg controls the final image base:
| Value | Base image | Use case |
|---|---|---|
| `web` (default) | `aspnet` | ASP.NET web applications |
| `app` | `runtime` | Console applications |
| `self-contained` | `runtime-deps` | Self-contained deployments |
### OpenAPI Linting
OpenAPI documents generated at build time are validated using [Spectral]. Configure rules in `.spectral.yaml` at the repository root.
## Recommendations
> Keep in mind that until the dotnet toolset handles generating new projects correctly, you will need to edit new *proj files and remove Version attributes from PackageReference entries. For more details consult [Central Package Management].
> Provided props files allow compiled models with EF Core 9+, to use them install `Microsoft.EntityFrameworkCore.Tasks` on all projects containing DbContext classes. (Not yet suited for production use, consult [Entity Framework Core MSBuild integration]).
[Central Package Management]: https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management
[Entity Framework Core MSBuild integration]: https://learn.microsoft.com/en-us/ef/core/cli/msbuild
[Spectral]: https://github.com/stoplightio/spectral

View File

@@ -11,6 +11,32 @@
"ms-dotnettools.csdevkit",
"ms-vscode-remote.remote-containers",
"seatonjiang.gitmoji-vscode",
"redhat.vscode-xml"
]
},
"settings": {
"files.associations": {
"nuget.config": "xml"
},
"json.schemas": [
{
"fileMatch": [
"xunit.runner.json"
],
"url": "https://xunit.net/schema/current/xunit.runner.schema.json"
},
{
"fileMatch": [
"stylecop.json"
],
"url": "https://raw.githubusercontent.com/bjornhellander/NewStyleCopAnalyzers/refs/heads/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json"
}
],
"editor.wordWrap": "on",
"[xml]": {
"editor.defaultFormatter": "redhat.vscode-xml"
},
"xml.format.maxLineWidth": 0,
"xml.format.splitAttributes": "preserve",
}
}

View File

@@ -30,7 +30,7 @@
<s:String x:Key="/Default/CodeInspection/XmlDocInspections/TypeAccessibility/@EntryValue">None</s:String>
<s:String x:Key="/Default/CodeInspection/XmlDocInspections/TypeMemberAccessibility/@EntryValue">None</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CommonFormatter/AUTODETECT_INDENT_SETTINGS/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">USE_TABS_ONLY</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">USE_SPACES</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_REGION/@EntryValue">0</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BETWEEN_USING_GROUPS/@EntryValue">1</s:Int64>
@@ -111,5 +111,10 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EUnitTestFramework_002EMigrations_002EEnableDisabledProvidersMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=Testing_0020Platform/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=VsTest/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/UnitTesting/TestingPlatformTestProvider/IgnoreProjectsSupportedByOtherProviders/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">False</s:Boolean>
</wpf:ResourceDictionary>

View File

@@ -1,4 +1,4 @@
<Solution>
<Solution>
<Folder Name="/Assets/">
<File Path="icon.png" />
<File Path="LICENSE.md" />
@@ -12,7 +12,6 @@
<File Path="Directory.Build.props" />
<File Path="Directory.Build.targets" />
<File Path="Directory.Packages.props" />
<File Path="dotnet.ruleset" />
<File Path="GitVersion.yml" />
<File Path="stylecop.json" />
<File Path="xunit.runner.json" />

175
docker/Dockerfile Normal file
View File

@@ -0,0 +1,175 @@
# syntax=docker/dockerfile:1.20
ARG RUNTIME_BASE=web
ARG CONFIGURATION=Release
ARG DOTNET_VERSION=10.0
ARG RUNTIME_ID=linux-musl-x64
FROM mcr.microsoft.com/dotnet/aspnet:${DOTNET_VERSION}-alpine-composite-extra AS web
WORKDIR /app
FROM mcr.microsoft.com/dotnet/runtime:${DOTNET_VERSION}-alpine-extra AS app
WORKDIR /app
FROM mcr.microsoft.com/dotnet/runtime-deps:${DOTNET_VERSION}-alpine-extra AS self-contained
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} AS sdk
ENV DOTNET_NOLOGO=1
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
ARG RUNTIME_ID
WORKDIR /source
FROM sdk AS restore
COPY --link .editorconfig .globalconfig global.json icon.png nuget.config *.slnx *.sln *.sln.DotSettings stylecop.json testconfig.json xunit.runner.json ./
COPY --link --parents ./**Directory*.props ./**Directory.*.targets ./
COPY --link --parents ./**.csproj ./**/packages.lock.json ./
RUN ls -lR .
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet restore
FROM sdk AS gitversion
RUN --mount=type=cache,target=/root/.nuget/packages dotnet tool install --global GitVersion.Tool
ENV PATH="${PATH}:/root/.dotnet/tools"
COPY --link --parents **/GitVersion.yml ./
COPY --link .git/ ./.git/
RUN dotnet-gitversion /output json > /version.json
FROM restore AS build
ARG CONFIGURATION
RUN mkdir -p /source/artifacts/OpenApi && \
mkdir -p /source/artifacts/bin && \
mkdir -p /source/artifacts/obj && \
mkdir -p /source/artifacts/nugets && \
mkdir -p /source/artifacts/upload
COPY LICENSE.md README.md ./
COPY --link src/ src/
COPY --link tests/ tests/
COPY --link samples/ samples/
COPY --link --from=gitversion /version.json ./version.json
RUN GV_MAJOR=$(grep -o '"Major": *[0-9]*' version.json | grep -o '[0-9]*') && \
GV_MINOR=$(grep -o '"Minor": *[0-9]*' version.json | grep -o '[0-9]*') && \
GV_PATCH=$(grep -o '"Patch": *[0-9]*' version.json | grep -o '[0-9]*') && \
GV_SEMVER=$(grep -o '"SemVer": *"[^"]*"' version.json | cut -d'"' -f4) && \
GV_FULLSEMVER=$(grep -o '"FullSemVer": *"[^"]*"' version.json | cut -d'"' -f4) && \
GV_INFORMATIONAL=$(grep -o '"InformationalVersion": *"[^"]*"' version.json | cut -d'"' -f4) && \
{ echo "GV_MAJOR=${GV_MAJOR}"; \
echo "GV_MINOR=${GV_MINOR}"; \
echo "GV_PATCH=${GV_PATCH}"; \
echo "GV_SEMVER=${GV_SEMVER}"; \
echo "GV_FULLSEMVER=${GV_FULLSEMVER}"; \
echo "GV_INFORMATIONAL=${GV_INFORMATIONAL}"; \
echo "VERSION_PROPS=\"/p:Version=\${GV_SEMVER} /p:AssemblyVersion=\${GV_MAJOR}.\${GV_MINOR}.\${GV_PATCH}.0 /p:FileVersion=\${GV_MAJOR}.\${GV_MINOR}.\${GV_PATCH}.0 /p:InformationalVersion=\${GV_INFORMATIONAL} /p:PackageVersion=\${GV_FULLSEMVER}\""; \
} > /source/version.env
RUN --mount=type=cache,target=/root/.nuget/packages \
. /source/version.env && \
eval dotnet build --no-restore --configuration ${CONFIGURATION} ${VERSION_PROPS}
FROM build AS test
ARG CONFIGURATION
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet test --no-build --configuration ${CONFIGURATION}
FROM node:lts-alpine AS openapi-lint
RUN npm install -g @stoplight/spectral-cli
WORKDIR /openapi
COPY --link .spectral.yaml /tmp/.spectral.yaml
COPY --link --from=build /source/artifacts/OpenApi/ .
RUN if ls *.json 1>/dev/null 2>&1; then \
spectral lint ./*.json -r /tmp/.spectral.yaml --fail-severity=error; \
else \
echo "No OpenAPI documents found, skipping lint"; \
fi
FROM test AS publish
ARG CONFIGURATION
ARG PUBLISHED_PROJECT
ARG RUNTIME_BASE
ARG PUBLISH_READY_TO_RUN=false
ENV PROJECT=src/${PUBLISHED_PROJECT}/${PUBLISHED_PROJECT}.csproj
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet restore --runtime ${RUNTIME_ID} ${PROJECT}
RUN --mount=type=cache,target=/root/.nuget/packages \
. /source/version.env && \
if [ "${RUNTIME_BASE}" = "self-contained" ]; then \
eval dotnet publish --configuration ${CONFIGURATION} \
--self-contained --runtime ${RUNTIME_ID} \
/p:PublishReadyToRun=${PUBLISH_READY_TO_RUN} \
/p:OpenApiGenerateDocuments=false \
${VERSION_PROPS} \
-o /app/publish ${PROJECT}; \
else \
eval dotnet publish --configuration ${CONFIGURATION} \
--no-self-contained --runtime ${RUNTIME_ID} \
/p:PublishReadyToRun=${PUBLISH_READY_TO_RUN} \
/p:OpenApiGenerateDocuments=false \
${VERSION_PROPS} \
-o /app/publish ${PROJECT}; \
fi
FROM build AS pack
RUN --mount=type=cache,target=/root/.nuget/packages \
. /source/version.env && \
eval dotnet pack --configuration Release ${VERSION_PROPS} --output /app/publish
FROM sdk AS nuget-push
WORKDIR /packages
COPY --link --from=pack /app/publish/*.nupkg .
ENTRYPOINT ["sh", "-c", "dotnet nuget push /packages/*.nupkg --source ${NUGET_SOURCE} --api-key ${NUGET_API_KEY} --skip-duplicate"]
CMD []
FROM build AS migration-bundle
RUN --mount=type=cache,target=/root/.dotnet/tool \
dotnet tool install --global dotnet-ef
ENV PATH="${PATH}:/root/.dotnet/tools"
ARG CONFIGURATION
ARG PUBLISHED_PROJECT
ARG DATABASE_PROJECT
ARG DBCONTEXT
ENV CONTEXT="${DBCONTEXT:+--context ${DBCONTEXT}}"
ENV EFPROJECT=src/${DATABASE_PROJECT}/${DATABASE_PROJECT}.csproj
ENV RUNNER=src/${PUBLISHED_PROJECT}/${PUBLISHED_PROJECT}.csproj
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet ef migrations bundle --configuration ${CONFIGURATION} --no-build --project ${EFPROJECT} --startup-project ${RUNNER} --output /app/migrations ${CONTEXT}
FROM sdk AS migrations
COPY --link --chown=root:root --chmod=755 --from=migration-bundle /app/migrations .
USER app
ENTRYPOINT ["./migrations"]
CMD []
FROM alpine:latest AS pyroscope-agent
ARG PYROSCOPE_VERSION
RUN mkdir -p /opt/pyroscope && \
if [ -n "${PYROSCOPE_VERSION}" ]; then \
wget -qO- "https://github.com/grafana/pyroscope-dotnet/releases/download/v${PYROSCOPE_VERSION}-pyroscope/pyroscope.${PYROSCOPE_VERSION}-musl-x86_64.tar.gz" \
| tar xz -C /opt/pyroscope; \
fi
FROM ${RUNTIME_BASE} AS final
ARG PUBLISHED_PROJECT
ARG RUNTIME_BASE
ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
COPY --link --from=publish --chown=root:root /app/publish .
COPY --link --from=openapi-lint --chown=root:root /openapi/ /app/openapi/
COPY --link --from=pyroscope-agent --chown=root:root /opt/pyroscope/ /pyroscope/
RUN if [ "${RUNTIME_BASE}" = "self-contained" ]; then \
chmod +x ${PUBLISHED_PROJECT}; \
printf '#!/bin/sh\nexec /app/%s "$@"\n' "${PUBLISHED_PROJECT}" > /app/entrypoint.sh; \
else \
printf '#!/bin/sh\nexec dotnet /app/%s.dll "$@"\n' "${PUBLISHED_PROJECT}" > /app/entrypoint.sh; \
fi && chmod +x /app/entrypoint.sh
USER $APP_UID
ENTRYPOINT ["/app/entrypoint.sh"]

View File

@@ -0,0 +1,79 @@
# Move this file to the root of the repo and drop the sample from the name to use it as the default compose file for development and testing.
services:
sample-cli:
build:
context: ..
dockerfile: docker/Dockerfile
target: final
args:
PUBLISHED_PROJECT: SampleCli
RUNTIME_BASE: app
restart: no
sample-api:
build:
context: ..
dockerfile: docker/Dockerfile
target: final
args:
PUBLISHED_PROJECT: SampleApi
RUNTIME_BASE: web
ports:
- "8080:8080"
environment:
- ASPNETCORE_URLS=http://+:8080
- ASPNETCORE_ENVIRONMENT=Development
sample-api-self-contained:
build:
context: ..
dockerfile: docker/Dockerfile
target: final
args:
PUBLISHED_PROJECT: SampleApi
RUNTIME_BASE: self-contained
depends_on:
sample-api-migrate:
condition: service_completed_successfully
ports:
- "8081:8080"
environment:
- ASPNETCORE_URLS=http://+:8080
- ASPNETCORE_ENVIRONMENT=Development
sample-api-migrate-database:
build:
context: ..
dockerfile: docker/Dockerfile
target: migrations
args:
PUBLISHED_PROJECT: SampleApi
EFPROJECT: Sample.Data
restart: no
ports:
- "8081:8080"
sample-api-pack:
build:
context: ..
dockerfile: docker/Dockerfile
target: pack
args:
CONFIGURATION: Release
DOTNET_VERSION: "10.0"
volumes:
- ../artifacts/nuget/:/packages/
entrypoint: [ "sh", "-c", "cp /app/publish/*.nupkg /packages/ 2>/dev/null; echo 'Packages copied to ./packages/'" ]
sample-api-publish:
build:
context: ..
dockerfile: docker/Dockerfile
target: publish
args:
PUBLISHED_PROJECT: SampleApi
RUNTIME_BASE: web
volumes:
- ../artifacts/upload/:/output/
entrypoint: [ "sh", "-c", "cp -r /app/publish/* /output/ && echo 'Published output copied to ./upload/'" ]

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Kritikos Ruleset" Description="Common roslyn analyser configuration" ToolsVersion="10.0">
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp" RuleNamespace="Microsoft.CodeAnalysis.CSharp">
<Rule Id="CS1573" Action="None" />
<Rule Id="CS1591" Action="None" />
<Rule Id="CS2008" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1303" Action="None" />
<Rule Id="CA1848" Action="None" />
<Rule Id="CA2007" Action="None" />
<Rule Id="CA1707" Action="None" />
</Rules>
<Rules AnalyzerId="SerilogAnalyzer" RuleNamespace="SerilogAnalyzer">
<Rule Id="Serilog004" Action="None" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1101" Action="None" />
<Rule Id="SA1633" Action="None" />
<Rule Id="SA1649" Action="None" />
</Rules>
</RuleSet>

8
global.json Normal file
View File

@@ -0,0 +1,8 @@
{
"sdk": {
"allowPrerelease": true
},
"test": {
"runner": "Microsoft.Testing.Platform"
}
}

16
nuget.config Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="kritikos.io" value="https://nuget.kritikos.io/v3/index.json" protocolVersion="3" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
<package pattern="Kritikos.*" />
</packageSource>
<packageSource key="kritikos.io">
<package pattern="Kritikos.*" />
</packageSource>
</packageSourceMapping>
</configuration>

View File

View File

@@ -1,14 +1,12 @@
<Project>
<ImportGroup
Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<ImportGroup Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</ImportGroup>
<PropertyGroup>
<IsPackable>false</IsPackable>
<PackAsTool>false</PackAsTool>
<IsPublishable>false</IsPublishable>
<NoWarn>$(NoWarn);SA1600</NoWarn>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,8 +1,6 @@
<Project>
<ImportGroup
Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<ImportGroup Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</ImportGroup>
<PropertyGroup Label="Package">

View File

@@ -33,7 +33,7 @@
"layoutRules": {
"newlineAtEndOfFile": "require",
"allowConsecutiveUsings": false,
"allowDoWhileOnClosingBrace": true
"allowDoWhileOnClosingBrace": false
},
"readabilityRules": {
"allowBuiltInTypeAliases": false

21
testconfig.json Normal file
View File

@@ -0,0 +1,21 @@
{
"platformOptions": {
},
"codeCoverage": {
"ExcludeAssembliesWithoutSources": "MissingAll",
"IncludeTestAssembly": false,
"SkipAutoProperties": true,
"AttributesExclude": [
"^System\\.ObsoleteAttribute$",
"^System\\.CodeDom\\.Compiler\\.GeneratedCodeAttribute$"
],
"SourcesExclude": [
".*\\\\tests\\\\.*\\.cs$",
".*\\\\samples\\\\.*\\.cs$"
]
},
"tunit": {
"long-running-test-seconds": 5
}
}

View File

@@ -1,32 +1,17 @@
<Project>
<ImportGroup
Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<ImportGroup Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</ImportGroup>
<PropertyGroup>
<IsPublishable>false</IsPublishable>
<IsTestProject>true</IsTestProject>
<NoWarn>$(NoWarn);SA1600</NoWarn>
</PropertyGroup>
<ItemGroup>
<Using Include="Xunit" />
<ItemGroup Label="xUnit Configuration" Condition="Exists('$(MSBuildThisFileDirectory)\..\xunit.runner.json') AND $(IsTestProject)=='True' AND @(PackageReference->StartsWith('xunit')->Count()) &gt; 0">
<Content Include="$(MSBuildThisFileDirectory)\..\xunit.runner.json" Link="xunit.runner.json"
CopyToOutputDirectory="PreserveNewest" Visible="false" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage" />
</ItemGroup>
<ItemGroup Label="Loggers">
<VSTestLogger
Include="trx%3BLogFileName=TestResults-$(TargetFramework)-$(MSBuildProjectName).trx" />
<VSTestLogger
Include="html%3BLogFileName=TestResults-$(TargetFramework)-$(MSBuildProjectName).html" />
</ItemGroup>
<PropertyGroup Condition="$(ContinuousIntegrationBuild) == 'true'">
<VSTestLogger>@(VSTestLogger)</VSTestLogger>
</PropertyGroup>
</Project>

View File