├── ServiceDemo
├── .nuke
├── src
│ └── ServiceDemo.Api
│ │ ├── Scripts
│ │ └── -.-
│ │ ├── appsettings.json
│ │ ├── WeatherForecast.cs
│ │ ├── ServiceDemo.Api.csproj
│ │ ├── appsettings.Development.LinuxOsx.json
│ │ ├── appsettings.Development.Windows.json
│ │ ├── Properties
│ │ └── launchSettings.json
│ │ ├── Controllers
│ │ └── WeatherForecastController.cs
│ │ ├── Startup.cs
│ │ └── Program.cs
├── terraform
│ ├── provider.tf
│ ├── data.tf
│ ├── backend.tf
│ ├── variables.tf
│ └── main.tf
├── build.cmd
├── .gitignore
├── tests
│ └── ServiceDemo.Api.Tests
│ │ ├── ThisTestShould.cs
│ │ └── ServiceDemo.Api.Tests.csproj
├── build
│ ├── .editorconfig
│ ├── _build.csproj
│ ├── _build.csproj.DotSettings
│ └── Build.cs
├── .github
│ └── workflows
│ │ └── ServiceDemoPipeline.yml
├── GitVersion.yml
├── initializeGitRepo.sh
├── docs
│ └── 0000-template.md
├── build.sh
├── .template.config
│ └── template.json
├── build.ps1
└── ServiceDemo.sln
├── .gitignore
├── GitVersion.yml
├── packageSettings.csproj
├── .github
└── workflows
│ └── PackageDotnetTemplate.yml
└── README.md
/ServiceDemo/.nuke:
--------------------------------------------------------------------------------
1 | ServiceDemo.sln
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/Scripts/-.-:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ServiceDemo/terraform/provider.tf:
--------------------------------------------------------------------------------
1 | # Configure the Azure Provider
2 | provider "azurerm" {
3 | version = "~>2.0"
4 |
5 | features {}
6 | }
--------------------------------------------------------------------------------
/ServiceDemo/build.cmd:
--------------------------------------------------------------------------------
1 | :; set -eo pipefail
2 | :; ./build.sh "$@"
3 | :; exit $?
4 |
5 | @ECHO OFF
6 | powershell -ExecutionPolicy ByPass -NoProfile %0\..\build.ps1 %*
7 |
--------------------------------------------------------------------------------
/ServiceDemo/terraform/data.tf:
--------------------------------------------------------------------------------
1 | data "azurerm_app_service_plan" "plan" {
2 | name = "TemplateDemo-ASP"
3 | resource_group_name = "TemplateDemoRG"
4 | }
5 |
6 | data "azurerm_subscription" "sub" {
7 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj/
2 | [Bb]in/
3 | .nuget/
4 | _ReSharper.*
5 | packages/
6 | artifacts/
7 | *.user
8 | *.suo
9 | *.userprefs
10 | *DS_Store
11 | *.sln.ide
12 | .vs/
13 | .vscode/
14 | .tmp/
15 | .idea/
16 | .terraform/
--------------------------------------------------------------------------------
/ServiceDemo/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj/
2 | [Bb]in/
3 | .nuget/
4 | _ReSharper.*
5 | packages/
6 | artifacts/
7 | *.user
8 | *.suo
9 | *.userprefs
10 | *DS_Store
11 | *.sln.ide
12 | .vs/
13 | .vscode/
14 | .tmp/
15 | .idea/
16 | .terraform/
--------------------------------------------------------------------------------
/ServiceDemo/terraform/backend.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | backend "azurerm" {
3 | resource_group_name = "TemplateDemoRG"
4 | storage_account_name = "templatedemostate"
5 | container_name = "states"
6 | key = "servicedemo.tfstate"
7 | }
8 | }
--------------------------------------------------------------------------------
/ServiceDemo/tests/ServiceDemo.Api.Tests/ThisTestShould.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ServiceDemo.Api.Tests
4 | {
5 | [TestFixture]
6 | public class ThisTestShould
7 | {
8 | [Test]
9 | public void AlwaysPass()
10 | {
11 | Assert.That(1, Is.EqualTo(1));
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "MinimumLevel": {
4 | "Default": "Debug",
5 | "Override": {
6 | "Microsoft": "Error",
7 | "System": "Error"
8 | }
9 | },
10 | "Properties": {
11 | "ApplicationName": "ServiceDemo.Api"
12 | }
13 | },
14 | "AllowedHosts": "*"
15 | }
16 |
--------------------------------------------------------------------------------
/ServiceDemo/terraform/variables.tf:
--------------------------------------------------------------------------------
1 | // ***** Default Variables *****
2 |
3 | variable "prefix" {
4 | description = "Custom prefix for the application services"
5 | type = string
6 | default = "service"
7 | }
8 |
9 | variable "location" {
10 | description = "The Azure Region in which the resource will be created."
11 | type = string
12 | default = "East US"
13 | }
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ServiceDemo.Api
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ServiceDemo/build/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_style_qualification_for_field = false:warning
3 | dotnet_style_qualification_for_property = false:warning
4 | dotnet_style_qualification_for_method = false:warning
5 | dotnet_style_qualification_for_event = false:warning
6 | dotnet_style_require_accessibility_modifiers = never:warning
7 |
8 | csharp_style_expression_bodied_methods = true:silent
9 | csharp_style_expression_bodied_properties = true:warning
10 | csharp_style_expression_bodied_indexers = true:warning
11 | csharp_style_expression_bodied_accessors = true:warning
12 |
--------------------------------------------------------------------------------
/ServiceDemo/tests/ServiceDemo.Api.Tests/ServiceDemo.Api.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ServiceDemo/build/_build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.0
6 |
7 | CS0649;CS0169
8 | ..
9 | ..
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/ServiceDemo.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/appsettings.Development.LinuxOsx.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "MinimumLevel": {
4 | "Default": "Debug",
5 | "Override": {
6 | "Microsoft": "Error",
7 | "System": "Error"
8 | }
9 | },
10 | "WriteTo": [
11 | {
12 | "Name": "File",
13 | "Args": {
14 | "path": "/var/log/ServiceDemo",
15 | "rollingInterval": "Day",
16 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}) {Message}{NewLine}{Exception}"
17 | }
18 | },
19 | {
20 | "Name": "Console",
21 | "Args": {
22 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}) {Message}{NewLine}{Exception}"
23 | }
24 | }
25 | ]
26 | }
27 | }
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/appsettings.Development.Windows.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "MinimumLevel": {
4 | "Default": "Debug",
5 | "Override": {
6 | "Microsoft": "Error",
7 | "System": "Error"
8 | }
9 | },
10 | "WriteTo": [
11 | {
12 | "Name": "File",
13 | "Args": {
14 | "path": "c:\\logs\\ServiceDemo\\log.txt",
15 | "rollingInterval": "Day",
16 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}) {Message}{NewLine}{Exception}"
17 | }
18 | },
19 | {
20 | "Name": "Console",
21 | "Args": {
22 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}) {Message}{NewLine}{Exception}"
23 | }
24 | }
25 | ]
26 | }
27 | }
--------------------------------------------------------------------------------
/ServiceDemo/.github/workflows/ServiceDemoPipeline.yml:
--------------------------------------------------------------------------------
1 | name: ServiceDemo Pipeline
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Fetch all history for all tags and branches
17 | run: git fetch --prune --unshallow
18 | - name: Nuke restore + build + test + publish
19 | run: ./build.sh --configuration Release -target compile+test+publish
20 | - name: Run Azure webapp deploy action using publish profile credentials
21 | uses: azure/webapps-deploy@v2
22 | with:
23 | app-name: service-servicedemo-api
24 | publish-profile: ${{ secrets.azureWebAppPublishProfile }}
25 | package: './artifacts/ServiceDemo.Api'
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: Mainline
2 | branches:
3 | main:
4 | tag: ''
5 | increment: Patch
6 | prevent-increment-of-merged-branch-version: true
7 | track-merge-target: false
8 | regex: ^main$
9 | source-branches:
10 | - develop
11 | - release
12 | tracks-release-branches: false
13 | is-release-branch: false
14 | is-mainline: true
15 | pre-release-weight: 55000
16 | release:
17 | source-branches:
18 | - develop
19 | - main
20 | - support
21 | - release
22 | feature:
23 | source-branches:
24 | - develop
25 | - main
26 | - release
27 | - feature
28 | - support
29 | - hotfix
30 | pull-request:
31 | source-branches:
32 | - develop
33 | - main
34 | - release
35 | - feature
36 | - support
37 | - hotfix
38 | hotfix:
39 | source-branches:
40 | - develop
41 | - main
42 | - support
43 | support:
44 | source-branches:
45 | - main
--------------------------------------------------------------------------------
/ServiceDemo/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: Mainline
2 | branches:
3 | main:
4 | tag: ''
5 | increment: Patch
6 | prevent-increment-of-merged-branch-version: true
7 | track-merge-target: false
8 | regex: ^main$
9 | source-branches:
10 | - develop
11 | - release
12 | tracks-release-branches: false
13 | is-release-branch: false
14 | is-mainline: true
15 | pre-release-weight: 55000
16 | release:
17 | source-branches:
18 | - develop
19 | - main
20 | - support
21 | - release
22 | feature:
23 | source-branches:
24 | - develop
25 | - main
26 | - release
27 | - feature
28 | - support
29 | - hotfix
30 | pull-request:
31 | source-branches:
32 | - develop
33 | - main
34 | - release
35 | - feature
36 | - support
37 | - hotfix
38 | hotfix:
39 | source-branches:
40 | - develop
41 | - main
42 | - support
43 | support:
44 | source-branches:
45 | - main
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:9981",
8 | "sslPort": 44312
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "weatherforecast",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "ServiceDemo.Api": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "weatherforecast",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packageSettings.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Template
5 | Dotnet.Template.Demo
6 | Dotnet.Template.Demo
7 | Olga Nelioubov
8 | Responsible for packaging the Dotnet Demo Template
9 | dotnet-new;template;buffalowebdevdemo
10 | netcoreapp3.1
11 |
12 | true
13 | false
14 | content
15 | true
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ServiceDemo/initializeGitRepo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Creating new local repository..."
4 | if ! git status > /dev/null 2>&1
5 | then
6 | git init
7 | else
8 | echo "Local repository already exists."
9 | fi
10 |
11 | echo -e "\nCreating [main] branch..."
12 | if git checkout -b main
13 | then
14 | echo -e "\nMaking initial commit..."
15 | git add .
16 | git commit -m "initial commit"
17 | git branch -M main
18 | else
19 | echo "Please see the following log for commit details."
20 | git log
21 | fi
22 |
23 | echo -e "\nSetting up remote repo..."
24 | if gh help > /dev/null 2>&1
25 | then
26 | tryCreateRemote=$(gh repo create ServiceDemo --public --confirm 2>/dev/null)
27 | if [ $? -eq 0 ]
28 | then
29 | echo "Created new repository $tryCreateRemote and added as remote."
30 | echo "If you're deploying to cloud resources, make sure to set those resources up before you push [main] to [origin]."
31 | else
32 | echo "Remote repository already exists."
33 | fi
34 | else
35 | echo "Please install GitHub CLI. For more info, visit https://cli.github.com/."
36 | fi
--------------------------------------------------------------------------------
/.github/workflows/PackageDotnetTemplate.yml:
--------------------------------------------------------------------------------
1 | name: Template Packaging Pipeline
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | publish:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Fetch all history for all tags and branches
17 | run: git fetch --prune --unshallow
18 | - name: Setup .NET Core
19 | uses: actions/setup-dotnet@v1
20 | with:
21 | dotnet-version: 3.1.101
22 | - name: Install GitVersion
23 | uses: gittools/actions/gitversion/setup@v0.9.2
24 | with:
25 | versionSpec: '5.2.x'
26 | - name: Use GitVersion
27 | id: gitversion
28 | uses: gittools/actions/gitversion/execute@v0.9.2
29 | - name: Pack the template
30 | run: dotnet pack ./packageSettings.csproj -o ./artifacts/ /p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} /p:RepositoryUrl=https://github.com/onelioubov/DotnetTemplateDemo
31 | - name: Push package to Artifactory
32 | run: dotnet nuget push ./artifacts/*.nupkg -k ${{ secrets.Artifactory }} -s https://onelioubov.jfrog.io/artifactory/api/nuget/v3/nuget-local --no-symbols true
--------------------------------------------------------------------------------
/ServiceDemo/docs/0000-template.md:
--------------------------------------------------------------------------------
1 | # 0000. Template
2 |
3 | Date: [yyyy-MM-dd]
4 | Modified: [yyyy-MM-dd]
5 |
6 | ## Status
7 |
8 | [Proposed, Accepted, Superseded by 0000]
9 |
10 | [A decision may be "proposed" if the project stakeholders haven't agreed with it yet, or "accepted" once it is agreed. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement.]
11 |
12 | ## Context
13 |
14 | [This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts.]
15 |
16 | ## Decision
17 |
18 | [This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..."]
19 |
20 | ## Consequences
21 |
22 | [This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future.]
23 |
24 | [More Info: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions)]
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace ServiceDemo.Api.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get()
28 | {
29 | var rng = new Random();
30 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/Startup.cs:
--------------------------------------------------------------------------------
1 | using HealthChecks.UI.Client;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Diagnostics.HealthChecks;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.Extensions.Configuration;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Hosting;
8 |
9 | namespace ServiceDemo.Api
10 | {
11 | public class Startup
12 | {
13 | private readonly IConfiguration _configuration;
14 | public Startup(IConfiguration configuration)
15 | {
16 | _configuration = configuration;
17 | }
18 |
19 | // This method gets called by the runtime. Use this method to add services to the container.
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | services.AddApplicationInsightsTelemetry();
23 | services.AddControllers();
24 | services.AddHealthChecks();
25 |
26 | // Register Swagger services
27 | services.AddSwaggerDocument();
28 | }
29 |
30 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
32 | {
33 | if (env.IsDevelopment())
34 | {
35 | app.UseDeveloperExceptionPage();
36 | }
37 |
38 | app.UseHttpsRedirection();
39 |
40 | app.UseRouting();
41 |
42 | app.UseAuthorization();
43 |
44 | // Register Swagger generator and UI middleware
45 | app.UseOpenApi();
46 | app.UseSwaggerUi3();
47 |
48 | app.UseEndpoints(endpoints =>
49 | {
50 | endpoints.MapControllers();
51 | endpoints.MapHealthChecks("/api/healthcheck", new HealthCheckOptions()
52 | {
53 | ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
54 | });
55 | });
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ServiceDemo/terraform/main.tf:
--------------------------------------------------------------------------------
1 | # Local variables for resource tagging
2 | locals {
3 | common_tags = {
4 | "Created On" = formatdate("DD MMM YYYY hh:mm ZZZ",timestamp())
5 | }
6 | }
7 |
8 | resource "azurerm_resource_group" "servicedemorg" {
9 | name = "ServiceDemoRG"
10 | location = var.location
11 | }
12 |
13 | # create application insights for servicedemo_api
14 | resource "azurerm_application_insights" "servicedemo_api_insights" {
15 | name = "${var.prefix}-servicedemo-api"
16 | location = var.location
17 | resource_group_name = azurerm_resource_group.servicedemorg.name
18 | application_type = "web"
19 | retention_in_days = 90
20 | }
21 |
22 | # create resource for servicedemo_api
23 | resource "azurerm_app_service" "servicedemo_api" {
24 | name = "${var.prefix}-servicedemo-api"
25 | location = var.location
26 | resource_group_name = azurerm_resource_group.servicedemorg.name
27 | app_service_plan_id = data.azurerm_app_service_plan.plan.id
28 |
29 | client_affinity_enabled = false
30 |
31 | site_config {
32 | default_documents = ["index.htm", "index.html", "hostingstart.html", "Default.htm", "Default.html"]
33 | health_check_path = "/api/healthcheck"
34 | linux_fx_version = "DOTNETCORE|3.1"
35 | }
36 |
37 | app_settings = {
38 | APPINSIGHTS_INSTRUMENTATIONKEY = join("", azurerm_application_insights.servicedemo_api_insights.*.instrumentation_key)
39 | WEBSITE_RUN_FROM_PACKAGE = "1"
40 | SCM_DO_BUILD_DURING_DEPLOYMENT = false
41 | ASPNETCORE_ENVIRONMENT = "Production"
42 | }
43 |
44 | lifecycle {
45 | ignore_changes = [tags]
46 | }
47 | tags = local.common_tags
48 |
49 | provisioner "local-exec" {
50 | command = "printf \"\\n\\nAdd the following publish profile as a new secret called azureWebAppPublishProfile in the GitHub repo prior to deploying to the new Azure Web App:\\n\\n$(az webapp deployment list-publishing-profiles --name ${azurerm_app_service.servicedemo_api.name} --resource-group ${azurerm_resource_group.servicedemorg.name} --subscription \"${data.azurerm_subscription.sub.display_name}\" --xml | sed 's/\\\\//g' | sed 's/^.\\(.*\\).$/\\1/')\\n\\n\""
51 | }
52 | }
--------------------------------------------------------------------------------
/ServiceDemo/src/ServiceDemo.Api/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.Hosting;
9 | using Serilog;
10 |
11 | namespace ServiceDemo.Api
12 | {
13 | public class Program
14 | {
15 | private static IConfiguration _configuration;
16 | public static void Main(string[] args)
17 | {
18 | try
19 | {
20 | var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
21 | var platform = env.Equals("Development") ?
22 | (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
23 | ".Windows" : ".LinuxOsx")
24 | : "";
25 | _configuration = new ConfigurationBuilder()
26 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
27 | .AddJsonFile(
28 | $"appsettings.{env ?? "Production"}{platform}.json",
29 | optional: true, reloadOnChange: true)
30 | .AddEnvironmentVariables()
31 | .AddCommandLine(args)
32 | .Build();
33 |
34 | Log.Logger = new LoggerConfiguration()
35 | .ReadFrom.Configuration(_configuration)
36 | .CreateLogger()
37 | .ForContext();
38 |
39 | Log.Information("Starting web host");
40 |
41 | CreateHostBuilder(args).Build().Run();
42 | }
43 | catch (Exception exception)
44 | {
45 | Log.Fatal(exception, "Site terminated");
46 | }
47 | finally
48 | {
49 | Log.Information("Ending web host");
50 |
51 | Log.CloseAndFlush();
52 | }
53 | }
54 |
55 | public static IHostBuilder CreateHostBuilder(string[] args) =>
56 | Host.CreateDefaultBuilder(args)
57 | .ConfigureWebHostDefaults(webBuilder =>
58 | {
59 | webBuilder
60 | .UseConfiguration(_configuration)
61 | .UseSerilog()
62 | .UseStartup();
63 | });
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ServiceDemo/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo $(bash --version 2>&1 | head -n 1)
4 |
5 | set -eo pipefail
6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
7 |
8 | ###########################################################################
9 | # CONFIGURATION
10 | ###########################################################################
11 |
12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
13 | TEMP_DIRECTORY="$SCRIPT_DIR//.tmp"
14 |
15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
17 | DOTNET_CHANNEL="Current"
18 |
19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1
20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
21 |
22 | ###########################################################################
23 | # EXECUTION
24 | ###########################################################################
25 |
26 | function FirstJsonValue {
27 | perl -nle 'print $1 if m{"'$1'": "([^"]+)",?}' <<< ${@:2}
28 | }
29 |
30 | # If global.json exists, load expected version
31 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
32 | DOTNET_VERSION=$(FirstJsonValue "version" $(cat "$DOTNET_GLOBAL_FILE"))
33 | if [[ "$DOTNET_VERSION" == "" ]]; then
34 | unset DOTNET_VERSION
35 | fi
36 | fi
37 |
38 | # If dotnet is installed locally, and expected version is not set or installation matches the expected version
39 | if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version 2>&1) == "$DOTNET_VERSION") ]]; then
40 | export DOTNET_EXE="$(command -v dotnet)"
41 | else
42 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
43 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
44 |
45 | # Download install script
46 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
47 | mkdir -p "$TEMP_DIRECTORY"
48 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
49 | chmod +x "$DOTNET_INSTALL_FILE"
50 |
51 | # Install by channel or version
52 | if [[ -z ${DOTNET_VERSION+x} ]]; then
53 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
54 | else
55 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
56 | fi
57 | fi
58 |
59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
60 |
61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false -nologo -clp:NoSummary --verbosity quiet
62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
63 |
--------------------------------------------------------------------------------
/ServiceDemo/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "Olga Nelioubov",
4 | "classifications": [
5 | "Api",
6 | "Code",
7 | "Reference"
8 | ],
9 | "identity": "Dotnet.Template.Demo",
10 | "name": "Demo Template",
11 | "shortName": "demo-template",
12 | "tags": {
13 | "language": "C#",
14 | "type": "item"
15 | },
16 | "sourceName": "ServiceDemo",
17 | "symbols": {
18 | "lowerCaseServiceName": {
19 | "type": "generated",
20 | "generator": "casing",
21 | "parameters": {
22 | "source": "name",
23 | "toLower": true
24 | },
25 | "replaces": "servicedemo"
26 | },
27 | "useAutomation": {
28 | "type": "parameter",
29 | "datatype": "bool",
30 | "description": "Use this to enable/disable Nuke, GitHub Actions, and Terraform",
31 | "defaultValue": "true"
32 | }
33 | },
34 | "sources":[{
35 | "modifiers": [
36 | {
37 | "exclude": [
38 | "**/[Bb]in/**",
39 | "**/[Oo]bj/**",
40 | ".template.config/**/*",
41 | "**/*.filelist",
42 | "**/*.user",
43 | "**/*.lock.json",
44 | "**/.vs/**",
45 | "**/.vscode/**",
46 | "**/.git/**",
47 | "packageSettings.csproj",
48 | "**/.tmp/**",
49 | ".idea/**",
50 | "**/artifacts/**",
51 | "**/.terraform/**"
52 | ]
53 | },
54 | {
55 | "exclude": [
56 | "**/.nuke",
57 | "**/build*",
58 | "**/build/**",
59 | "**/.github/**",
60 | "**/terraform/**"
61 | ],
62 | "condition": "(!useAutomation)"
63 | }
64 | ]
65 | }],
66 | "postActions": [
67 | {
68 | "condition": "(OS != \"Windows_NT\")",
69 | "description": "Make scripts executable",
70 | "manualInstructions": [{ "text": "Run 'chmod +x *.sh'" }],
71 | "actionId": "CB9A6CF3-4F5C-4860-B9D2-03A574959774",
72 | "args": {
73 | "+x": "*.sh"
74 | },
75 | "continueOnError": true
76 | },
77 | {
78 | "actionId": "3A7C4B45-1F5D-4A30-959A-51B88E82B5D2",
79 | "description": "Sets up the local and remote git repo and creates an initial commit to [main] by calling initializeGitRepo.sh",
80 | "args": {
81 | "executable": "initializeGitRepo.sh",
82 | "args": "",
83 | "redirectStandardOutput": "false"
84 | },
85 | "manualInstructions": [{
86 | "text": "Run 'initializeGitRepo.sh'"
87 | }],
88 | "continueOnError": false
89 | }
90 | ],
91 | "placeholderFilename": "-.-"
92 | }
--------------------------------------------------------------------------------
/ServiceDemo/build.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param(
3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
4 | [string[]]$BuildArguments
5 | )
6 |
7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
8 |
9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
11 |
12 | ###########################################################################
13 | # CONFIGURATION
14 | ###########################################################################
15 |
16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
17 | $TempDirectory = "$PSScriptRoot\\.tmp"
18 |
19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json"
20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
21 | $DotNetChannel = "Current"
22 |
23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
25 |
26 | ###########################################################################
27 | # EXECUTION
28 | ###########################################################################
29 |
30 | function ExecSafe([scriptblock] $cmd) {
31 | & $cmd
32 | if ($LASTEXITCODE) { exit $LASTEXITCODE }
33 | }
34 |
35 | # If global.json exists, load expected version
36 | if (Test-Path $DotNetGlobalFile) {
37 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
38 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
39 | $DotNetVersion = $DotNetGlobal.sdk.version
40 | }
41 | }
42 |
43 | # If dotnet is installed locally, and expected version is not set or installation matches the expected version
44 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
45 | (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) {
46 | $env:DOTNET_EXE = (Get-Command "dotnet").Path
47 | }
48 | else {
49 | $DotNetDirectory = "$TempDirectory\dotnet-win"
50 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
51 |
52 | # Download install script
53 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
54 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
55 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
56 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
57 |
58 | # Install by channel or version
59 | if (!(Test-Path variable:DotNetVersion)) {
60 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
61 | } else {
62 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
63 | }
64 | }
65 |
66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
67 |
68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false -nologo -clp:NoSummary --verbosity quiet }
69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
70 |
--------------------------------------------------------------------------------
/ServiceDemo/ServiceDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDemo.Api", "src\ServiceDemo.Api\ServiceDemo.Api.csproj", "{77E611A3-9A32-4A7E-A9E9-9B130A249D5A}"
7 | EndProject
8 | #if (useAutomation)
9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{9B21D099-26CB-4D25-8C5E-F9ED07A0C18B}"
10 | EndProject
11 | #endif
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDemo.Api.Tests", "tests\ServiceDemo.Api.Tests\ServiceDemo.Api.Tests.csproj", "{BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Debug|x64 = Debug|x64
18 | Debug|x86 = Debug|x86
19 | Release|Any CPU = Release|Any CPU
20 | Release|x64 = Release|x64
21 | Release|x86 = Release|x86
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {9B21D099-26CB-4D25-8C5E-F9ED07A0C18B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {9B21D099-26CB-4D25-8C5E-F9ED07A0C18B}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|x64.ActiveCfg = Debug|Any CPU
32 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|x64.Build.0 = Debug|Any CPU
33 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|x86.ActiveCfg = Debug|Any CPU
34 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Debug|x86.Build.0 = Debug|Any CPU
35 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|x64.ActiveCfg = Release|Any CPU
38 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|x64.Build.0 = Release|Any CPU
39 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|x86.ActiveCfg = Release|Any CPU
40 | {77E611A3-9A32-4A7E-A9E9-9B130A249D5A}.Release|x86.Build.0 = Release|Any CPU
41 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|x64.ActiveCfg = Debug|Any CPU
44 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|x64.Build.0 = Debug|Any CPU
45 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|x86.ActiveCfg = Debug|Any CPU
46 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Debug|x86.Build.0 = Debug|Any CPU
47 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|x64.ActiveCfg = Release|Any CPU
50 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|x64.Build.0 = Release|Any CPU
51 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|x86.ActiveCfg = Release|Any CPU
52 | {BF8ED7F0-02FB-4145-9C7C-4AD832B937BE}.Release|x86.Build.0 = Release|Any CPU
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/ServiceDemo/build/_build.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | DO_NOT_SHOW
3 | DO_NOT_SHOW
4 | DO_NOT_SHOW
5 | DO_NOT_SHOW
6 | Implicit
7 | Implicit
8 | ExpressionBody
9 | 0
10 | NEXT_LINE
11 | True
12 | False
13 | 120
14 | IF_OWNER_IS_SINGLE_LINE
15 | WRAP_IF_LONG
16 | False
17 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
19 | True
20 | True
21 | True
22 | True
23 | True
24 | True
25 | True
26 | True
27 | True
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DotnetTemplateDemo
2 | This is a demo of how custom .NET templates can be used to quickly create a functioning and deployable API. This includes:
3 | * The creation of a local and remote Git repository on GitHub during template-invocation.
4 | * Requires [GitHub CLI](https://cli.github.com/) configured for your GitHub account.
5 | * Currently only works on Linux and MacOS, but I have plans to make it friendly to Windows as well (unless you have WSL, in which case, you should be good).
6 |
7 | * A [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) module to create a Resource Group, Web App, and Application Insights resources within Microsoft Azure.
8 | * Please note that this assumes you have the following cloud resources for this to be deployed:
9 | - Azure Subscription.
10 | - Azure Storage Account with a container for the Terraform state files.
11 | - Azure App Service Plan to deploy the Web App to.
12 |
13 | The reason these resources need to be pre-created is because I'm assuming you would be running multiple web apps out of a single App Service Plan (that is managed outside of the actual repo containing your API code). If that's not the case, an App Service Plan resource can always be added to the provided Terraform template.
14 | * When you're ready to create your resources, make sure you're logged into your account via the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) and set the Subscription you want to use; then, just run a `terraform init` and `terraform apply` to get the resources created.
15 | * A build pipeline to compile and test your code (and generate a client when ready) using [Nuke](http://www.nuke.build/docs/getting-started/setup.html).
16 | * A GitHub Action to build and release your API to the resources created by Terraform. This runs automatically on push to the remote [main] branch.
17 | * Please note that there is still a manual step to include the publish profile from the newly-created Azure Web App (you should be able to get it as an output when you create the web app via Terraform) as a [GitHub repo secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets) named azureWebAppPublishProfile. This needs to be done before you push to [main].
18 | * A GitHub Action to package the template itself and push it to a NuGet feed for further distribution.
19 |
20 | #### Some Caveats
21 | - Ultimately, this template has a few hard-coded values, such as ones for the NuGet feed and the pre-created resources in Azure (the App Service Plan and Storage Account names). This is for the purposes of showing how it can be tailored to your org (including naming conventions and some standard variables used throughout the company), where the "org" in this specific case is my GitHub account, NuGet feed, and Azure Subscription.
22 |
23 | - This is not for general use as it will not work for you out of the box (unless you actually change the aforementioned hard-coded values to your own).
24 |
25 | - This is for the purposes of demonstrating how custom .NET templates can be used and is not a statement on how the code within the template should actually be structured (even though _some_ of my opinions do apply here). There are probably a few things in here that you don't want to do in a live application. However, there are some resources below pertaining to the items I've implemented in this repo; they're still good to have in your toolbox, even if the demo implementation of them does not necessarily show their best usage.
26 |
27 | # Resources
28 | ## General .NET Tempate
29 | [Microsoft documentation for .NET Tempates](https://docs.microsoft.com/en-us/dotnet/core/tools/custom-templates)
30 | [Microsoft custom template tutorial](https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-item-template)
31 | [.NET Template Parameter Generators](https://github.com/dotnet/templating/wiki/Available-Parameter-Generators)
32 | [Post Action Registry](https://github.com/dotnet/templating/wiki/Post-Action-Registry)
33 | [Testing Your Templates](https://github.com/dotnet/templating/wiki/Testing-your-templates)
34 | [Useful Microsoft blog post](https://devblogs.microsoft.com/dotnet/how-to-create-your-own-templates-for-dotnet-new/)
35 |
36 | ## Miscellaneous Resources
37 | [Recommended .NET project structure](https://gist.github.com/davidfowl/ed7564297c61fe9ab814)
38 | [Microsoft doc for NSwag Swagger specification](https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-nswag?view=aspnetcore-3.1&tabs=netcore-cli)
39 | [Microsoft doc for healthchecks](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health)
40 | [Build automation with Nuke](http://www.nuke.build/)
41 | [GitVersion configuration](https://gitversion.readthedocs.io/en/latest/input/docs/configuration/)
42 | [Serilog for logging](https://serilog.net/)
43 | [Using appsettings.json for Serilog configuration](https://github.com/serilog/serilog-settings-configuration)
44 | [Deploying Azure App Service with GitHub Actions](https://docs.microsoft.com/en-us/azure/app-service/deploy-github-actions)
45 | [Documenting Architecture Decisions](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions)
46 | [Another ADR Resource](https://github.com/joelparkerhenderson/architecture_decision_record)
47 | [GitHub CLI](https://cli.github.com/)
48 |
49 | If you have any questions about this repo, feel free to reach out!
--------------------------------------------------------------------------------
/ServiceDemo/build/Build.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Nuke.Common;
4 | using Nuke.Common.Execution;
5 | using Nuke.Common.Git;
6 | using Nuke.Common.IO;
7 | using Nuke.Common.ProjectModel;
8 | using Nuke.Common.Tooling;
9 | using Nuke.Common.Tools.Docker;
10 | using Nuke.Common.Tools.DotNet;
11 | using Nuke.Common.Tools.GitVersion;
12 | using Nuke.Common.Tools.NSwag;
13 | using Nuke.Common.Utilities.Collections;
14 | using static Nuke.Common.EnvironmentInfo;
15 | using static Nuke.Common.IO.FileSystemTasks;
16 | using static Nuke.Common.IO.PathConstruction;
17 | using static Nuke.Common.Tools.Docker.DockerTasks;
18 | using static Nuke.Common.Tools.DotNet.DotNetTasks;
19 | using static Nuke.Common.Tools.NSwag.NSwagTasks;
20 |
21 | [CheckBuildProjectConfigurations]
22 | [UnsetVisualStudioEnvironmentVariables]
23 | class Build : NukeBuild
24 | {
25 | public static int Main () => Execute(x => x.Compile);
26 |
27 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
28 | readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
29 |
30 | [Solution] readonly Solution Solution;
31 | [GitRepository] readonly GitRepository GitRepository;
32 | [GitVersion] readonly GitVersion GitVersion;
33 |
34 | AbsolutePath SourceDirectory => RootDirectory / "src";
35 | AbsolutePath TestsDirectory => RootDirectory / "tests";
36 | AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
37 |
38 | Target Clean => _ => _
39 | .Before(Restore)
40 | .Executes(() =>
41 | {
42 | SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory);
43 | TestsDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory);
44 | EnsureCleanDirectory(ArtifactsDirectory);
45 | });
46 |
47 | Target Restore => _ => _
48 | .Executes(() =>
49 | {
50 | DotNetRestore(s => s
51 | .SetProjectFile(Solution));
52 | });
53 |
54 | Target Compile => _ => _
55 | .DependsOn(Restore)
56 | .Executes(() =>
57 | {
58 | DotNetBuild(s => s
59 | .SetProjectFile(Solution)
60 | .SetConfiguration(Configuration)
61 | .SetAssemblyVersion(GitVersion.AssemblySemVer)
62 | .SetFileVersion(GitVersion.AssemblySemFileVer)
63 | .SetInformationalVersion(GitVersion.InformationalVersion)
64 | .EnableNoRestore());
65 | });
66 |
67 | Target Test => _ => _
68 | .After(Compile)
69 | .Executes(() =>
70 | {
71 | var testProjects = Solution.AllProjects.Where(project =>
72 | project.Path.ToString().Contains(TestsDirectory.ToString()));
73 | DotNetTest(t => t
74 | .EnableNoBuild()
75 | .SetConfiguration(Configuration)
76 | .SetWorkingDirectory(TestsDirectory)
77 | .CombineWith(testProjects, (x, p) => x
78 | .SetProjectFile(p)
79 | .SetLogger("nunit;LogFilePath=TestResults/" + p.Name + "-Result.xml")));
80 | });
81 |
82 | Target GenerateClient => _ => _
83 | .DependsOn(Compile)
84 | .Executes(() =>
85 | {
86 | var clientDir = SourceDirectory;
87 | var clientProjDir = clientDir / "ServiceDemo.Client";
88 | EnsureCleanDirectory(clientProjDir);
89 |
90 | var openApiPath = clientDir / "ServiceDemo.json";
91 |
92 | NSwagAspNetCoreToOpenApi(x => x
93 | .SetNSwagRuntime("NetCore31")
94 | .SetAssembly(SourceDirectory / "ServiceDemo.Api" / "bin" / Configuration.ToString() / "netcoreapp3.1" / "ServiceDemo.Api.dll")
95 | .SetDocumentName("v1")
96 | .EnableUseDocumentProvider()
97 | .SetOutputType(SchemaType.OpenApi3)
98 | .SetOutput(openApiPath)
99 | );
100 |
101 | NSwagSwaggerToCSharpClient(x => x
102 | .SetNSwagRuntime("NetCore31")
103 | .SetInput(openApiPath)
104 | .SetOutput(clientProjDir / "ServiceDemo.Client.cs")
105 | .SetNamespace("ServiceDemo.Clients")
106 | .SetGenerateClientInterfaces(true)
107 | .SetGenerateExceptionClasses(true)
108 | .SetExceptionClass("{controller}ClientException")
109 | );
110 |
111 | var version = GitRepository.Branch.Equals("main", StringComparison.OrdinalIgnoreCase) ? GitVersion.MajorMinorPatch : GitVersion.NuGetVersionV2;
112 |
113 | DotNet($"new classlib -o {clientProjDir}", workingDirectory: clientProjDir);
114 | DeleteFile(clientProjDir / "Class1.cs");
115 | DotNet($"add package Newtonsoft.Json", workingDirectory: clientProjDir);
116 | DotNet("add package System.ComponentModel.Annotations", workingDirectory: clientProjDir);
117 |
118 | DotNetPack(x => x
119 | .SetProject(clientProjDir)
120 | .SetOutputDirectory(ArtifactsDirectory)
121 | .SetConfiguration(Configuration)
122 | .SetVersion(version)
123 | .SetIncludeSymbols(true)
124 | );
125 | });
126 | Target Publish => _ => _
127 | .After(Test)
128 | .Executes(() =>
129 | {
130 | var projectSolution = Solution.AllProjects.Where(p =>
131 | !p.Name.Contains("Tests")
132 | && !p.Name.Contains("build")
133 | && p.Is(ProjectType.CSharpProject));
134 |
135 | DotNetPublish(s => s
136 | .SetConfiguration(Configuration)
137 | .SetAssemblyVersion(GitVersion.AssemblySemVer)
138 | .SetFileVersion(GitVersion.AssemblySemFileVer)
139 | .SetInformationalVersion(GitVersion.InformationalVersion)
140 | .EnableNoRestore()
141 | .CombineWith(projectSolution, (x, p) => x
142 | .SetProject(p)
143 | .SetOutput(ArtifactsDirectory / p.Name)));
144 | });
145 | }
146 |
--------------------------------------------------------------------------------