├── .config └── dotnet-tools.json ├── .github └── workflows │ ├── continuous.yml │ └── deploy.yml ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── Readme.md ├── Try.Dotnet.Affected.Dependency ├── Dependency.cs └── Try.Dotnet.Affected.Dependency.csproj ├── Try.Dotnet.Affected.ProxyStoryApi ├── Program.cs ├── Properties │ └── launchSettings.json ├── Try.Dotnet.Affected.ProxyStoryApi.csproj ├── Try.Dotnet.Affected.ProxyStoryApi.csproj.user ├── appsettings.Development.json └── appsettings.json ├── Try.Dotnet.Affected.Sidestory ├── Program.cs └── Try.Dotnet.Affected.Sidestory.csproj ├── Try.Dotnet.Affected.StoryApi ├── Program.cs ├── Properties │ └── launchSettings.json ├── Try.Dotnet.Affected.StoryApi.csproj ├── Try.Dotnet.Affected.StoryApi.csproj.user ├── appsettings.Development.json └── appsettings.json ├── Try.Dotnet.Affected.Tests ├── GlobalUsings.cs ├── Try.Dotnet.Affected.Tests.csproj └── UnitTest1.cs ├── Try.Dotnet.Affected.sln ├── Try.Dotnet.Affected ├── Program.cs └── Try.Dotnet.Affected.csproj ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── .editorconfig ├── Build.cs ├── Components │ ├── Azure.Targets.cs │ ├── Dotnet.Affected.Targets.cs │ ├── Pulumi.Targets.cs │ ├── Try.Dotnet.Affected.Build.cs │ ├── Try.Dotnet.Affected.Dependency.Build.cs │ ├── Try.Dotnet.Affected.DeployBase.cs │ ├── Try.Dotnet.Affected.ProxyStoryApi.Build.cs │ ├── Try.Dotnet.Affected.Sidestory.Build.cs │ ├── Try.Dotnet.Affected.StoryApi.Build.cs │ └── Try.Dotnet.Affected.Tests.Build.cs ├── Configuration.cs ├── Directory.Build.props ├── Directory.Build.targets ├── Properties │ └── launchSettings.json ├── _build.csproj └── _build.csproj.DotSettings └── screenshots └── BuildTargets.png /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-affected": { 6 | "version": "3.2.0", 7 | "commands": [ 8 | "dotnet-affected" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.github/workflows/continuous.yml: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # 3 | # 4 | # This code was generated. 5 | # 6 | # - To turn off auto-generation set: 7 | # 8 | # [GitHubActions (AutoGenerate = false)] 9 | # 10 | # - To trigger manual generation invoke: 11 | # 12 | # nuke --generate-configuration GitHubActions_continuous --host GitHubActions 13 | # 14 | # 15 | # ------------------------------------------------------------------------------ 16 | 17 | name: continuous 18 | 19 | on: [push] 20 | 21 | jobs: 22 | windows-latest: 23 | name: windows-latest 24 | runs-on: windows-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | - name: 'Cache: .nuke/temp, ~/.nuget/packages' 30 | uses: actions/cache@v3 31 | with: 32 | path: | 33 | .nuke/temp 34 | ~/.nuget/packages 35 | key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }} 36 | - name: 'Run: CompileSolution' 37 | run: ./build.cmd CompileSolution 38 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: [push] 4 | 5 | jobs: 6 | windows-latest: 7 | name: windows-latest 8 | runs-on: windows-latest 9 | environment: 'Dev' 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - name: 'Cache: .nuke/temp, ~/.nuget/packages' 15 | uses: actions/cache@v3 16 | with: 17 | path: | 18 | .nuke/temp 19 | ~/.nuget/packages 20 | key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }} 21 | - name: 'Run: DeploySolution' 22 | run: ./build.cmd DeploySolution 23 | env: 24 | ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} 25 | ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} 26 | ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} 27 | ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} 28 | PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | bin/ 3 | obj/ 4 | affected.proj 5 | output/ 6 | Try.Dotnet.Affected.Tests/TestResults 7 | Try.Dotnet.Affected.sln.DotSettings.user 8 | affected.txt 9 | .nuke/temp 10 | artifacts/ -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "$ref": "#/definitions/build", 4 | "title": "Build Schema", 5 | "definitions": { 6 | "build": { 7 | "type": "object", 8 | "properties": { 9 | "ARM_CLIENT_ID": { 10 | "type": "string", 11 | "default": "Secrets must be entered via 'nuke :secrets [profile]'" 12 | }, 13 | "ARM_CLIENT_SECRET": { 14 | "type": "string", 15 | "default": "Secrets must be entered via 'nuke :secrets [profile]'" 16 | }, 17 | "ARM_TENANT_ID": { 18 | "type": "string", 19 | "default": "Secrets must be entered via 'nuke :secrets [profile]'" 20 | }, 21 | "Configuration": { 22 | "type": "string", 23 | "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", 24 | "enum": [ 25 | "Debug", 26 | "Release" 27 | ] 28 | }, 29 | "Continue": { 30 | "type": "boolean", 31 | "description": "Indicates to continue a previously failed build attempt" 32 | }, 33 | "Help": { 34 | "type": "boolean", 35 | "description": "Shows the help text for this build assembly" 36 | }, 37 | "Host": { 38 | "type": "string", 39 | "description": "Host for execution. Default is 'automatic'", 40 | "enum": [ 41 | "AppVeyor", 42 | "AzurePipelines", 43 | "Bamboo", 44 | "Bitbucket", 45 | "Bitrise", 46 | "GitHubActions", 47 | "GitLab", 48 | "Jenkins", 49 | "Rider", 50 | "SpaceAutomation", 51 | "TeamCity", 52 | "Terminal", 53 | "TravisCI", 54 | "VisualStudio", 55 | "VSCode" 56 | ] 57 | }, 58 | "NoLogo": { 59 | "type": "boolean", 60 | "description": "Disables displaying the NUKE logo" 61 | }, 62 | "Partition": { 63 | "type": "string", 64 | "description": "Partition to use on CI" 65 | }, 66 | "Plan": { 67 | "type": "boolean", 68 | "description": "Shows the execution plan (HTML)" 69 | }, 70 | "Profile": { 71 | "type": "array", 72 | "description": "Defines the profiles to load", 73 | "items": { 74 | "type": "string" 75 | } 76 | }, 77 | "Root": { 78 | "type": "string", 79 | "description": "Root directory during build execution" 80 | }, 81 | "Skip": { 82 | "type": "array", 83 | "description": "List of targets to be skipped. Empty list skips all dependencies", 84 | "items": { 85 | "type": "string", 86 | "enum": [ 87 | "AzLogin", 88 | "Clean", 89 | "CleanTryDotnetAffectedProxyStoryApi", 90 | "CleanTryDotnetAffectedStoryApi", 91 | "CompileSolution", 92 | "CompileTryDotnetAffected", 93 | "CompileTryDotnetAffectedDependency", 94 | "CompileTryDotnetAffectedProxyStoryApi", 95 | "CompileTryDotnetAffectedSidestory", 96 | "CompileTryDotnetAffectedStoryApi", 97 | "CompileTryDotnetAffectedTests", 98 | "DeploySolution", 99 | "DeployStackTryDotnetAffectedDeployBase", 100 | "DeployStackTryDotnetAffectedProxyStoryApi", 101 | "DeployStackTryDotnetAffectedStoryApi", 102 | "DotnetAffected", 103 | "InstallAzureCli", 104 | "InstallPulumi", 105 | "ProvisionPulumi", 106 | "PublishSolution", 107 | "PublishStackTryDotnetAffectedProxyStoryApi", 108 | "PublishStackTryDotnetAffectedStoryApi", 109 | "PublishTryDotnetAffected", 110 | "PublishTryDotnetAffectedProxyStoryApi", 111 | "PublishTryDotnetAffectedSidestory", 112 | "PublishTryDotnetAffectedStoryApi", 113 | "Restore", 114 | "RunTestsSolution", 115 | "RunTestsTryDotnetAffectedTests" 116 | ] 117 | } 118 | }, 119 | "Target": { 120 | "type": "array", 121 | "description": "List of targets to be invoked. Default is '{default_target}'", 122 | "items": { 123 | "type": "string", 124 | "enum": [ 125 | "AzLogin", 126 | "Clean", 127 | "CleanTryDotnetAffectedProxyStoryApi", 128 | "CleanTryDotnetAffectedStoryApi", 129 | "CompileSolution", 130 | "CompileTryDotnetAffected", 131 | "CompileTryDotnetAffectedDependency", 132 | "CompileTryDotnetAffectedProxyStoryApi", 133 | "CompileTryDotnetAffectedSidestory", 134 | "CompileTryDotnetAffectedStoryApi", 135 | "CompileTryDotnetAffectedTests", 136 | "DeploySolution", 137 | "DeployStackTryDotnetAffectedDeployBase", 138 | "DeployStackTryDotnetAffectedProxyStoryApi", 139 | "DeployStackTryDotnetAffectedStoryApi", 140 | "DotnetAffected", 141 | "InstallAzureCli", 142 | "InstallPulumi", 143 | "ProvisionPulumi", 144 | "PublishSolution", 145 | "PublishStackTryDotnetAffectedProxyStoryApi", 146 | "PublishStackTryDotnetAffectedStoryApi", 147 | "PublishTryDotnetAffected", 148 | "PublishTryDotnetAffectedProxyStoryApi", 149 | "PublishTryDotnetAffectedSidestory", 150 | "PublishTryDotnetAffectedStoryApi", 151 | "Restore", 152 | "RunTestsSolution", 153 | "RunTestsTryDotnetAffectedTests" 154 | ] 155 | } 156 | }, 157 | "Verbosity": { 158 | "type": "string", 159 | "description": "Logging verbosity during build execution. Default is 'Normal'", 160 | "enum": [ 161 | "Minimal", 162 | "Normal", 163 | "Quiet", 164 | "Verbose" 165 | ] 166 | } 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "Try.Dotnet.Affected.sln" 4 | } 5 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Dotnet Affected 🔍 & Nuke ☢️ 2 | 3 | 4 | This project shows an example on how to integrate [Dotnet Affected](https://github.com/leonardochaia/dotnet-affected) with [Nuke Build](https://nuke.build/). 5 | 6 | shields 7 | 8 | 🪟Project Screenshots: 9 | -- 10 | project-screenshot 11 | 12 | 💡Features 13 | -- 14 | 15 | * Use dotnet-affected to trigger specific build tasks 16 | * Seperate build files for different projects 17 | * Pulumi deployment to Azure using Pulumi Automation 18 | 19 | 🛠️ Installation Steps: 20 | -- 21 | 1. Clone repository 22 | ```bash 23 | git clone https://github.com/jwdb/Try.Dotnet.Affected/ 24 | ``` 25 | 2. Install Nuke 26 | ```bash 27 | dotnet tool install Nuke.GlobalTool --global 28 | ``` 29 | 3. Nuke ☢️ it from orbit 🚀 30 | 31 | ```bash 32 | nuke 33 | ``` 34 | 35 | Project Structure 36 | -- 37 | The project consists of multiple example projects and a build project. 38 | Example projects: 39 | 40 | * `/Try.Dotnet.Affected/` This is a plain console app with a dependency to `/Try.Dotnet.Affected.Dependency/` 41 | * `/Try.Dotnet.Affected.Sidestory/` Another plain console app with no dependencies. 42 | * `/Try.Dotnet.Affected.Tests/` This has a reference to `/Try.Dotnet.Affected/` and serves as a test-project for it. 43 | * `/Try.Dotnet.Affected.StoryApi/` A simple WebApi with no external dependencies 44 | * `/Try.Dotnet.Affected.ProxyStoryApi/` This project has a implicit dependenc on the StoryApi through a app setting. 45 | 46 | All these projects have their own Companion `*.build.cs` under `/build/Components/`. These files describe how the project should be *Compiled*, *Tested*, *Published* and *Deployed*. 47 | 48 | The description on how this is done is contained into individual targets. 49 | 50 | --- 51 | ### Compile 52 | Every `*.Build.cs` file has a **Compile** target that defines on how the project is built. This target has a dependency on The **DotnetAffected** Target. 53 | 54 | This target is *TriggeredBy the **CompileSolution** Target* but only ran if *the project is included in the **ProjectsToBuild** array* . 55 | 56 | --- 57 | ### Test 58 | When a project has Tests, a **Test** target can be added. Yet again this has a dependency on the **DotnetAffected** Target but also on the **Compile** Target. 59 | 60 | This target is *TriggeredBy the **RunTestsSolution** Target* but only ran if *the project is included in the **ProjectsToBuild** array*. 61 | 62 | --- 63 | ### Publish 64 | If a project can be Published. a **Publish** target can be added. In the same way as the previous targt, this has a dependency on **DotnetAffected** and the **Compile** Target. 65 | 66 | This target is *TriggeredBy the **RunTestsSolution** Target* but only ran if *the project is included in the **ProjectsToBuild** array*. 67 | 68 | --- 69 | 70 | ### Deploy 71 | The most complicated step. The step is divided into two parts: **DeployStack** and **PublishStack**. 72 | 73 | #### **DeployStack** 74 | This target is to setup the infrastructure using *Pulumi*. 75 | This function makes it possible to add extra items to the Pulumi stack: 76 | ```csharp 77 | IPulumiTargets.Stack.With(() => { }) 78 | ``` 79 | This function either has a `Action` as parameter or a `Func<{[name] = Output}>` where each property of the anonymous type of the type `Output` can be used in other Deploy targets using: `IPulumiTargets.Stack.GetOutput("ResourceGroupName")`. 80 | 81 | This Target has a *DependentFor* constraint on **IPulumiTargets.ProvisionPulumi**. Using this constraint this Target will be ran before Provisioning has happened. 82 | 83 | Using a combination of *After* and *Before* this Target can be placed on a certain place in the pulumi stack. 84 | 85 | --- 86 | ⚠️ Warning: This target has no dependency on **DotnetAffected** because otherwise the resource would be removed by Pulumi when it has not changed. 87 | 88 | --- 89 | #### **PublishStack** 90 | With this target the *published* project is *deployed* to the provisioned *stack*. 91 | This target yet again dependent on **DotnetAffected** but also on **IPulumiTargets.ProvisionPulumi**. 92 | 93 | This target is *TriggeredBy the **PublishSoltuin** Target* but only ran if *the project is included in the **ProjectsToBuild** array*. -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Dependency/Dependency.cs: -------------------------------------------------------------------------------- 1 | namespace Try.Dotnet.Affected.Dependency 2 | { 3 | public class Dependency 4 | { 5 | public Dependency() 6 | { 7 | Console.WriteLine("It depends."); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Dependency/Try.Dotnet.Affected.Dependency.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Add services to the container. 4 | 5 | var app = builder.Build(); 6 | 7 | // Configure the HTTP request pipeline. 8 | 9 | app.UseHttpsRedirection(); 10 | 11 | var apiUrl = app.Configuration.GetValue("StoryApiUrl"); 12 | Console.WriteLine(apiUrl); 13 | 14 | var client = new HttpClient() 15 | { 16 | BaseAddress = new Uri(apiUrl), 17 | }; 18 | 19 | app.MapGet("/story", async () => $"Proxy: {await client.GetStringAsync("/story")}"); 20 | 21 | app.Run(); 22 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "http": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "story", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "dotnetRunMessages": true, 11 | "applicationUrl": "http://localhost:5282" 12 | }, 13 | "https": { 14 | "commandName": "Project", 15 | "launchBrowser": true, 16 | "launchUrl": "story", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development", 19 | "ASPNETCORE_URLS": "http://localhost:5001/" 20 | }, 21 | "dotnetRunMessages": true, 22 | "applicationUrl": "https://localhost:7033;http://localhost:5282" 23 | }, 24 | "IIS Express": { 25 | "commandName": "IISExpress", 26 | "launchBrowser": true, 27 | "launchUrl": "story", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | } 32 | }, 33 | "$schema": "https://json.schemastore.org/launchsettings.json", 34 | "iisSettings": { 35 | "windowsAuthentication": false, 36 | "anonymousAuthentication": true, 37 | "iisExpress": { 38 | "applicationUrl": "http://localhost:37690", 39 | "sslPort": 44346 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/Try.Dotnet.Affected.ProxyStoryApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/Try.Dotnet.Affected.ProxyStoryApi.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | https 5 | 6 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.ProxyStoryApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "StoryApiUrl": "https://localhost:7187/" 10 | } 11 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Sidestory/Program.cs: -------------------------------------------------------------------------------- 1 | Console.WriteLine("Once upon a time"); 2 | Console.WriteLine("We were free"); 3 | Console.WriteLine("But everything changed"); 4 | Console.WriteLine("When the fire nation attacked"); -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Sidestory/Try.Dotnet.Affected.Sidestory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Add services to the container. 4 | 5 | var app = builder.Build(); 6 | 7 | // Configure the HTTP request pipeline. 8 | 9 | app.UseHttpsRedirection(); 10 | 11 | var summaries = new[] 12 | { 13 | "a cat", "a dog", "a wild ferret", "the moon", "martians", "kudos", "electrons", "an android", "a gray sky", "rain" 14 | }; 15 | 16 | app.MapGet("/story", () => $"Once upon a time there was a story about {summaries[Random.Shared.Next(0, summaries.Length)]}"); 17 | 18 | app.Run(); 19 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:8116", 8 | "sslPort": 44352 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "story", 17 | "applicationUrl": "http://localhost:5137", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "story", 27 | "applicationUrl": "https://localhost:7187;http://localhost:5137", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "story", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/Try.Dotnet.Affected.StoryApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/Try.Dotnet.Affected.StoryApi.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | https 5 | 6 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.StoryApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Tests/Try.Dotnet.Affected.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | namespace Try.Dotnet.Affected.Tests; 2 | 3 | public class UnitTest1 4 | { 5 | [Fact] 6 | public void Test1() 7 | { 8 | _ = new Dependency.Dependency(); 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Try.Dotnet.Affected.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34003.232 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Dotnet.Affected", "Try.Dotnet.Affected\Try.Dotnet.Affected.csproj", "{4540F94A-F4EF-43D6-9741-C1CBCE2DABAD}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Dotnet.Affected.Dependency", "Try.Dotnet.Affected.Dependency\Try.Dotnet.Affected.Dependency.csproj", "{88E3DAA9-E14A-4142-A389-CBB3F8F08D28}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Dotnet.Affected.Sidestory", "Try.Dotnet.Affected.Sidestory\Try.Dotnet.Affected.Sidestory.csproj", "{1B944318-BB51-4142-BA16-23B34D2CC8FD}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{A17BF7C7-E450-4FE7-B68C-76A9169422AF}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Dotnet.Affected.Tests", "Try.Dotnet.Affected.Tests\Try.Dotnet.Affected.Tests.csproj", "{80314E3D-C575-4895-9E0F-F0DD7F64BEC7}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Dotnet.Affected.StoryApi", "Try.Dotnet.Affected.StoryApi\Try.Dotnet.Affected.StoryApi.csproj", "{BE3DBDBD-38EE-47CA-AB12-EFE6D9CD04B9}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Try.Dotnet.Affected.ProxyStoryApi", "Try.Dotnet.Affected.ProxyStoryApi\Try.Dotnet.Affected.ProxyStoryApi.csproj", "{3581007E-84AD-4745-BE95-BEBD7A619B5A}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {4540F94A-F4EF-43D6-9741-C1CBCE2DABAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4540F94A-F4EF-43D6-9741-C1CBCE2DABAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4540F94A-F4EF-43D6-9741-C1CBCE2DABAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4540F94A-F4EF-43D6-9741-C1CBCE2DABAD}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {88E3DAA9-E14A-4142-A389-CBB3F8F08D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {88E3DAA9-E14A-4142-A389-CBB3F8F08D28}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {88E3DAA9-E14A-4142-A389-CBB3F8F08D28}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {88E3DAA9-E14A-4142-A389-CBB3F8F08D28}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {1B944318-BB51-4142-BA16-23B34D2CC8FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {1B944318-BB51-4142-BA16-23B34D2CC8FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {1B944318-BB51-4142-BA16-23B34D2CC8FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {1B944318-BB51-4142-BA16-23B34D2CC8FD}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {A17BF7C7-E450-4FE7-B68C-76A9169422AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {A17BF7C7-E450-4FE7-B68C-76A9169422AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {80314E3D-C575-4895-9E0F-F0DD7F64BEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {80314E3D-C575-4895-9E0F-F0DD7F64BEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {80314E3D-C575-4895-9E0F-F0DD7F64BEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {80314E3D-C575-4895-9E0F-F0DD7F64BEC7}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {BE3DBDBD-38EE-47CA-AB12-EFE6D9CD04B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {BE3DBDBD-38EE-47CA-AB12-EFE6D9CD04B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {BE3DBDBD-38EE-47CA-AB12-EFE6D9CD04B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {BE3DBDBD-38EE-47CA-AB12-EFE6D9CD04B9}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {3581007E-84AD-4745-BE95-BEBD7A619B5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {3581007E-84AD-4745-BE95-BEBD7A619B5A}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {3581007E-84AD-4745-BE95-BEBD7A619B5A}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {3581007E-84AD-4745-BE95-BEBD7A619B5A}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(ExtensibilityGlobals) = postSolution 57 | SolutionGuid = {B84A7895-873E-4B6F-AA52-3C74762C822A} 58 | EndGlobalSection 59 | EndGlobal 60 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected/Program.cs: -------------------------------------------------------------------------------- 1 | using Try.Dotnet.Affected.Dependency; 2 | 3 | Console.WriteLine("What's the story Kenneth?"); 4 | _ = new Dependency(); 5 | -------------------------------------------------------------------------------- /Try.Dotnet.Affected/Try.Dotnet.Affected.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /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\\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "STS" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function ExecSafe([scriptblock] $cmd) { 32 | & $cmd 33 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 34 | } 35 | 36 | # If dotnet CLI is installed globally and it matches requested version, use for execution 37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 40 | } 41 | else { 42 | # Download install script 43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 47 | 48 | # If global.json exists, load expected version 49 | if (Test-Path $DotNetGlobalFile) { 50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 52 | $DotNetVersion = $DotNetGlobal.sdk.version 53 | } 54 | } 55 | 56 | # Install by channel or version 57 | $DotNetDirectory = "$TempDirectory\dotnet-win" 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { 69 | & $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null 70 | & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null 71 | } 72 | 73 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 74 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 75 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 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//.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="STS" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | export DOTNET_MULTILEVEL_LOOKUP=0 22 | 23 | ########################################################################### 24 | # EXECUTION 25 | ########################################################################### 26 | 27 | function FirstJsonValue { 28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 29 | } 30 | 31 | # If dotnet CLI is installed globally and it matches requested version, use for execution 32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 33 | export DOTNET_EXE="$(command -v dotnet)" 34 | else 35 | # Download install script 36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 37 | mkdir -p "$TEMP_DIRECTORY" 38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 39 | chmod +x "$DOTNET_INSTALL_FILE" 40 | 41 | # If global.json exists, load expected version 42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 44 | if [[ "$DOTNET_VERSION" == "" ]]; then 45 | unset DOTNET_VERSION 46 | fi 47 | fi 48 | 49 | # Install by channel or version 50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 51 | if [[ -z ${DOTNET_VERSION+x} ]]; then 52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 53 | else 54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 55 | fi 56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 57 | fi 58 | 59 | echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" 60 | 61 | if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "$NUKE_ENTERPRISE_TOKEN" != "" ]]; then 62 | "$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true 63 | "$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true 64 | fi 65 | 66 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 67 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 68 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /build/Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Components; 5 | using Nuke.Common; 6 | using Nuke.Common.CI.GitHubActions; 7 | 8 | [GitHubActions( 9 | "continuous", 10 | GitHubActionsImage.WindowsLatest, 11 | On = new[] { GitHubActionsTrigger.Push }, 12 | FetchDepth = 0, 13 | InvokedTargets = new[] { nameof(CompileSolution) })] 14 | class Build : NukeBuild, 15 | IDotnetAffectedTargets, 16 | IPulumiTargets, 17 | IAzureTargets, 18 | ITryDotnetAffectedDeployBase, 19 | ITryDotnetAffectedBuild, 20 | ITryDotnetAffectedDependencyBuild, 21 | ITryDotnetAffectedSidestoryBuild, 22 | ITryDotnetAffectedTestsBuild, 23 | ITryDotnetAffectedStoryApiBuild, 24 | ITryDotnetAffectedProxyStoryApiBuild 25 | { 26 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] 27 | public static readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; 28 | 29 | public static Dictionary ProjectsToBuild = new(); 30 | 31 | public static int Main() => Execute(x => x.CompileSolution); 32 | 33 | public Target Clean => definition => definition 34 | .Executes(() => 35 | { 36 | File.Delete("affected.json"); 37 | }); 38 | 39 | public Target Restore => definition => definition 40 | .DependsOn(Clean) 41 | .Executes(() => { }); 42 | 43 | public Target CompileSolution => definition => definition 44 | .DependsOn(Restore) 45 | .DependsOn(c => c.DotnetAffected) 46 | .Executes(() => { }); 47 | 48 | public Target PublishSolution => definition => definition 49 | .DependsOn(CompileSolution) 50 | .Executes(() => { }); 51 | 52 | public Target RunTestsSolution => definition => definition 53 | .DependsOn(CompileSolution) 54 | .Executes(() => { }); 55 | 56 | public Target DeploySolution => definition => definition 57 | .DependsOn(PublishSolution) 58 | .Executes(() => { }); 59 | 60 | public static Func BaseTarget => projectName => 61 | definition => definition 62 | .DependsOn(c => c.DotnetAffected) 63 | .OnlyWhenDynamic(() => ProjectsToBuild.ContainsKey(projectName), $"{projectName} is affected"); 64 | } -------------------------------------------------------------------------------- /build/Components/Azure.Targets.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.Tooling; 3 | using Nuke.Common.Tools.Chocolatey; 4 | 5 | // ReSharper disable InconsistentNaming 6 | namespace Components; 7 | 8 | interface IAzureTargets : INukeBuild 9 | { 10 | [PathVariable("az")] static Tool Az; 11 | 12 | [Parameter][Secret] string ARM_CLIENT_ID => TryGetValue(() => ARM_CLIENT_ID); 13 | [Parameter][Secret] string ARM_CLIENT_SECRET => TryGetValue(() => ARM_CLIENT_SECRET); 14 | [Parameter][Secret] string ARM_TENANT_ID => TryGetValue(() => ARM_TENANT_ID); 15 | 16 | Target InstallAzureCli => definition => definition 17 | .Executes(() => ChocolateyTasks.Chocolatey("upgrade azure-cli -y")); 18 | 19 | Target AzLogin => definition => definition 20 | .DependsOn(InstallAzureCli) 21 | .Executes(() => 22 | { 23 | Az(new Arguments() 24 | .Add("login") 25 | .Add("--service-principal") 26 | .Add("-u {value}", ARM_CLIENT_ID, secret: true) 27 | .Add("-p {value}", ARM_CLIENT_SECRET, secret: true) 28 | .Add("--tenant {value}", ARM_TENANT_ID, secret: true) 29 | .RenderForExecution()); 30 | }); 31 | } -------------------------------------------------------------------------------- /build/Components/Dotnet.Affected.Targets.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.Json; 4 | using Nuke.Common; 5 | using Nuke.Common.CI.AzurePipelines; 6 | using Nuke.Common.CI.GitHubActions; 7 | using Nuke.Common.Tooling; 8 | using Nuke.Common.Tools.DotNet; 9 | 10 | namespace Components; 11 | interface IDotnetAffectedTargets : INukeBuild 12 | { 13 | AzurePipelines AzurePipelines => AzurePipelines.Instance; 14 | GitHubActions GitHubActions => GitHubActions.Instance; 15 | 16 | // ReSharper disable once ClassNeverInstantiated.Local 17 | record AffectedJson(string Name, string FilePath); 18 | 19 | public Target DotnetAffected => definition => definition 20 | .Executes(() => 21 | { 22 | var args = new Arguments() 23 | .Add("tool") 24 | .Add("run") 25 | .Add("dotnet-affected") 26 | .Add("--format json") 27 | .Add("--verbose", condition: Verbosity >= Verbosity.Normal); 28 | 29 | if (AzurePipelines != null) 30 | { 31 | args.Add("--from {value}", AzurePipelines.PullRequestSourceBranch) 32 | .Add("--to {value}", AzurePipelines.PullRequestTargetBranch); 33 | } 34 | 35 | if (GitHubActions != null) 36 | { 37 | if (GitHubActions.IsPullRequest) 38 | { 39 | args.Add("--from {value}", GitHubActions.HeadRef) 40 | .Add("--to {value}", GitHubActions.BaseRef); 41 | } 42 | else 43 | { 44 | args.Add("--from {value}", GitHubActions.GitHubEvent["before"]?.ToString()); 45 | } 46 | } 47 | 48 | DotNetTasks.DotNetToolRestore(); 49 | DotNetTasks.DotNet(args.ToString(), exitHandler: DotnetToolExitHandler); 50 | 51 | if (!File.Exists("affected.json")) 52 | { 53 | return; 54 | } 55 | 56 | var toBuildJson = File.ReadAllText("affected.json"); 57 | var affectedProjects = JsonSerializer.Deserialize(toBuildJson); 58 | foreach (var project in affectedProjects) 59 | { 60 | Build.ProjectsToBuild.Add(project.Name, project.FilePath); 61 | } 62 | }); 63 | 64 | 65 | void DotnetToolExitHandler(IProcess obj) 66 | { 67 | switch (obj.ExitCode) 68 | { 69 | case 0: // Success 70 | case 166: // No changed projects 71 | return; 72 | default: 73 | throw new Exception(); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /build/Components/Pulumi.Targets.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Pulumi.Automation; 3 | using System.Collections.Generic; 4 | using System; 5 | using System.Linq; 6 | using Nuke.Common.Tools.Chocolatey; 7 | using Pulumi; 8 | using Log = Serilog.Log; 9 | 10 | namespace Components; 11 | 12 | [ParameterPrefix("Pulumi")] 13 | interface IPulumiTargets : INukeBuild 14 | { 15 | public static PulumiStack Stack = new(); 16 | 17 | Target InstallPulumi => definition => definition.Executes(() => ChocolateyTasks.Chocolatey("upgrade pulumi -y")); 18 | 19 | Target ProvisionPulumi => definition => definition 20 | .DependsOn(InstallPulumi) 21 | .Executes(async () => 22 | { 23 | var program = Stack.Build(); 24 | 25 | using var workspace = await LocalWorkspace.CreateOrSelectStackAsync( 26 | new InlineProgramArgs("Try.Dotnet.Affected", "WebApp", program)); 27 | 28 | await workspace.UpAsync(new() 29 | { 30 | OnStandardOutput = Log.Information, 31 | OnStandardError = Log.Information 32 | }); 33 | }); 34 | } 35 | 36 | public class PulumiStack 37 | { 38 | readonly List Actions = new(); 39 | readonly Dictionary Outputs = new (); 40 | 41 | 42 | public PulumiStack With(Action action) 43 | { 44 | Actions.Add(action); 45 | return this; 46 | } 47 | 48 | public PulumiStack With(Func action) 49 | { 50 | Actions.Add(() => 51 | { 52 | var result = action(); 53 | var outputs = typeof(T).GetProperties().Where(c => c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(Output<>)); 54 | foreach (var item in outputs) 55 | { 56 | Outputs.Add(item.Name, item.GetValue(result)); 57 | } 58 | }); 59 | return this; 60 | } 61 | 62 | public Output GetOutput(string name) 63 | { 64 | return Outputs[name] as Output; 65 | } 66 | 67 | public PulumiFn Build() => 68 | PulumiFn.Create(() => { 69 | Actions.ForEach(action => action()); 70 | }); 71 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.Build.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.IO; 3 | using Nuke.Common.Tools.DotNet; 4 | using Nuke.Common.Tools.MSBuild; 5 | 6 | namespace Components; 7 | 8 | interface ITryDotnetAffectedBuild : INukeBuild 9 | { 10 | const string ProjectName = "Try.Dotnet.Affected"; 11 | public AbsolutePath OutputDirectory => RootDirectory / "output" / ProjectName; 12 | 13 | Target CompileTryDotnetAffected => definition => definition 14 | .Inherit(Build.BaseTarget(ProjectName)) 15 | .TriggeredBy(c => c.CompileSolution) 16 | .Executes(() => 17 | { 18 | var project = Build.ProjectsToBuild[ProjectName]; 19 | 20 | MSBuildTasks.MSBuild(settings => settings 21 | .SetTargetPath(project) 22 | .SetTargets("Build") 23 | .SetConfiguration(Build.Configuration) 24 | .EnableRestore() 25 | .EnableNodeReuse()); 26 | }); 27 | 28 | Target PublishTryDotnetAffected => definition => definition 29 | .Inherit(Build.BaseTarget(ProjectName)) 30 | .DependsOn(CompileTryDotnetAffected) 31 | .TriggeredBy(c => c.PublishSolution) 32 | .Executes(() => 33 | { 34 | var project = Build.ProjectsToBuild[ProjectName]; 35 | 36 | DotNetTasks.DotNetPublish(s => s 37 | .SetOutput(OutputDirectory) 38 | .SetProject(project) 39 | .SetConfiguration(Build.Configuration) 40 | .EnableNoRestore()); 41 | }); 42 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.Dependency.Build.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.Tools.MSBuild; 3 | 4 | namespace Components; 5 | 6 | interface ITryDotnetAffectedDependencyBuild : INukeBuild 7 | { 8 | const string ProjectName = "Try.Dotnet.Affected.Dependency"; 9 | 10 | Target CompileTryDotnetAffectedDependency => definition => definition 11 | .Inherit(Build.BaseTarget(ProjectName)) 12 | .TriggeredBy(c => c.CompileSolution) 13 | .Executes(() => 14 | { 15 | var project = Build.ProjectsToBuild[ProjectName]; 16 | 17 | MSBuildTasks.MSBuild(settings => settings 18 | .SetTargetPath(project) 19 | .SetTargets("Build") 20 | .SetConfiguration(Build.Configuration) 21 | .EnableRestore() 22 | .EnableNodeReuse()); 23 | }); 24 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.DeployBase.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Pulumi.AzureNative.Resources; 3 | 4 | namespace Components; 5 | 6 | interface ITryDotnetAffectedDeployBase : INukeBuild 7 | { 8 | public const string AzureRegion = "westeurope"; 9 | 10 | Target DeployStackTryDotnetAffectedDeployBase => definition => definition 11 | .DependsOn(c => c.AzLogin) 12 | .DependentFor(c => c.ProvisionPulumi) 13 | .Triggers(c => c.ProvisionPulumi) 14 | .Executes(() => 15 | IPulumiTargets.Stack.With(() => 16 | { 17 | var resourceGroup = new ResourceGroup("poc-nuke-pulumi", new() 18 | { 19 | Location = AzureRegion, 20 | }); 21 | 22 | return new { ResourceGroupName = resourceGroup.Name }; 23 | }) 24 | ); 25 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.ProxyStoryApi.Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Net.Http.Headers; 5 | using System.Net.Http; 6 | using System.Text; 7 | using Nuke.Common; 8 | using Nuke.Common.IO; 9 | using Nuke.Common.Tools.DotNet; 10 | using Nuke.Common.Tools.MSBuild; 11 | using Pulumi.AzureNative.Web; 12 | using Pulumi.AzureNative.Web.Inputs; 13 | 14 | namespace Components; 15 | 16 | interface ITryDotnetAffectedProxyStoryApiBuild : INukeBuild 17 | { 18 | const string ProjectName = "Try.Dotnet.Affected.ProxyStoryApi"; 19 | AbsolutePath OutputDirectory => RootDirectory / "output" / ProjectName; 20 | AbsolutePath PublishArtifact => RootDirectory / "artifacts" / ProjectName / "api.zip"; 21 | 22 | static string PublishingUsername; 23 | static string PublishingPassword; 24 | static string PublishingAppServiceName; 25 | 26 | Target CleanTryDotnetAffectedProxyStoryApi => definition => definition 27 | .Inherit(Build.BaseTarget(ProjectName)) 28 | .TriggeredBy(c => c.Clean) 29 | .Executes(() => 30 | { 31 | Directory.CreateDirectory(Path.GetDirectoryName(PublishArtifact)!); 32 | File.Delete(PublishArtifact); 33 | }); 34 | 35 | Target CompileTryDotnetAffectedProxyStoryApi => definition => definition 36 | .Inherit(Build.BaseTarget(ProjectName)) 37 | .TriggeredBy(c => c.CompileSolution) 38 | .Executes(() => 39 | { 40 | var project = Build.ProjectsToBuild[ProjectName]; 41 | 42 | MSBuildTasks.MSBuild(settings => settings 43 | .SetTargetPath(project) 44 | .SetTargets("Build") 45 | .SetConfiguration(Build.Configuration) 46 | .EnableRestore() 47 | .EnableNodeReuse()); 48 | }); 49 | 50 | Target PublishTryDotnetAffectedProxyStoryApi => definition => definition 51 | .Inherit(Build.BaseTarget(ProjectName)) 52 | .DependsOn(CompileTryDotnetAffectedProxyStoryApi, CleanTryDotnetAffectedProxyStoryApi) 53 | .TriggeredBy(c => c.PublishSolution) 54 | .Executes(() => 55 | { 56 | var project = Build.ProjectsToBuild[ProjectName]; 57 | 58 | DotNetTasks.DotNetPublish(s => s 59 | .SetOutput(OutputDirectory) 60 | .SetProject(project) 61 | .SetConfiguration(Build.Configuration) 62 | .EnableNoRestore()); 63 | 64 | Directory.CreateDirectory(Path.GetDirectoryName(PublishArtifact)!); 65 | ZipFile.CreateFromDirectory(OutputDirectory, PublishArtifact); 66 | }); 67 | 68 | Target DeployStackTryDotnetAffectedProxyStoryApi => definition => definition 69 | .After(c => c.DeployStackTryDotnetAffectedDeployBase) 70 | .After(c => c.DeployStackTryDotnetAffectedStoryApi) 71 | .Before(c => c.ProvisionPulumi) 72 | .DependentFor(c => c.ProvisionPulumi) 73 | .Executes(() => 74 | IPulumiTargets.Stack.With(() => 75 | { 76 | var resourceGroupName = IPulumiTargets.Stack.GetOutput("ResourceGroupName"); 77 | var storyApiName = IPulumiTargets.Stack.GetOutput("StoryApiName").Apply(c => $"https://{c}.azurewebsites.net"); 78 | var appService = new WebApp("app-proxy", new WebAppArgs 79 | { 80 | ResourceGroupName = resourceGroupName, 81 | Location = ITryDotnetAffectedDeployBase.AzureRegion, 82 | SiteConfig = new SiteConfigArgs 83 | { 84 | AppSettings = 85 | { 86 | new NameValuePairArgs 87 | { 88 | Name = "StoryApiUrl", 89 | Value = storyApiName, 90 | } 91 | } 92 | } 93 | }); 94 | 95 | var publishingCredentials = ListWebAppPublishingCredentials.Invoke(new() 96 | { 97 | ResourceGroupName = resourceGroupName, 98 | Name = appService.Name 99 | }); 100 | 101 | publishingCredentials.Apply(c => PublishingUsername = c.PublishingUserName); 102 | publishingCredentials.Apply(c => PublishingPassword = c.PublishingPassword); 103 | appService.Name.Apply(c => PublishingAppServiceName = c); 104 | }) 105 | ); 106 | 107 | Target PublishStackTryDotnetAffectedProxyStoryApi => definition => definition 108 | .Inherit(Build.BaseTarget(ProjectName)) 109 | .DependsOn(DeployStackTryDotnetAffectedProxyStoryApi, PublishTryDotnetAffectedProxyStoryApi) 110 | .DependsOn(c => c.ProvisionPulumi) 111 | .TriggeredBy(c => c.PublishSolution) 112 | .Executes(async () => 113 | { 114 | var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{PublishingUsername}:{PublishingPassword}")); 115 | 116 | await using var package = File.OpenRead(PublishArtifact); 117 | using var httpClient = new HttpClient(); 118 | httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64Auth); 119 | await httpClient.PostAsync($"https://{PublishingAppServiceName}.scm.azurewebsites.net/api/zipdeploy", 120 | new StreamContent(package)); 121 | }); 122 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.Sidestory.Build.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.IO; 3 | using Nuke.Common.Tools.DotNet; 4 | using Nuke.Common.Tools.MSBuild; 5 | 6 | namespace Components; 7 | 8 | interface ITryDotnetAffectedSidestoryBuild : INukeBuild 9 | { 10 | const string ProjectName = "Try.Dotnet.Affected.Sidestory"; 11 | public AbsolutePath OutputDirectory => RootDirectory / "output" / ProjectName; 12 | 13 | Target CompileTryDotnetAffectedSidestory => definition => definition 14 | .Inherit(Build.BaseTarget(ProjectName)) 15 | .TriggeredBy(c => c.CompileSolution) 16 | .Executes(() => 17 | { 18 | var project = Build.ProjectsToBuild[ProjectName]; 19 | 20 | MSBuildTasks.MSBuild(settings => settings 21 | .SetTargetPath(project) 22 | .SetTargets("Build") 23 | .SetConfiguration(Build.Configuration) 24 | .EnableRestore() 25 | .EnableNodeReuse()); 26 | }); 27 | 28 | Target PublishTryDotnetAffectedSidestory => definition => definition 29 | .DependsOn(CompileTryDotnetAffectedSidestory) 30 | .Inherit(Build.BaseTarget(ProjectName)) 31 | .TriggeredBy(c => c.PublishSolution) 32 | .Executes(() => 33 | { 34 | var project = Build.ProjectsToBuild[ProjectName]; 35 | DotNetTasks.DotNetPublish(s => s 36 | .SetOutput(OutputDirectory) 37 | .SetProject(project) 38 | .SetConfiguration(Build.Configuration) 39 | .EnableNoRestore()); 40 | }); 41 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.StoryApi.Build.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Net.Http.Headers; 5 | using System.Net.Http; 6 | using System.Text; 7 | using Nuke.Common; 8 | using Nuke.Common.IO; 9 | using Nuke.Common.Tools.DotNet; 10 | using Nuke.Common.Tools.MSBuild; 11 | using Pulumi.AzureNative.Web; 12 | 13 | namespace Components; 14 | 15 | interface ITryDotnetAffectedStoryApiBuild : INukeBuild 16 | { 17 | const string ProjectName = "Try.Dotnet.Affected.StoryApi"; 18 | AbsolutePath OutputDirectory => RootDirectory / "output" / ProjectName; 19 | AbsolutePath PublishArtifact => RootDirectory / "artifacts" / ProjectName / "api.zip"; 20 | 21 | static string PublishingUsername; 22 | static string PublishingPassword; 23 | static string PublishingAppServiceName; 24 | 25 | Target CleanTryDotnetAffectedStoryApi => definition => definition 26 | .Inherit(Build.BaseTarget(ProjectName)) 27 | .TriggeredBy(c => c.Clean) 28 | .Executes(() => 29 | { 30 | Directory.CreateDirectory(Path.GetDirectoryName(PublishArtifact)!); 31 | File.Delete(PublishArtifact); 32 | }); 33 | 34 | 35 | Target CompileTryDotnetAffectedStoryApi => definition => definition 36 | .Inherit(Build.BaseTarget(ProjectName)) 37 | .TriggeredBy(c => c.CompileSolution) 38 | .Executes(() => 39 | { 40 | var project = Build.ProjectsToBuild[ProjectName]; 41 | 42 | MSBuildTasks.MSBuild(settings => settings 43 | .SetTargetPath(project) 44 | .SetTargets("Build") 45 | .SetConfiguration(Build.Configuration) 46 | .EnableRestore() 47 | .EnableNodeReuse()); 48 | }); 49 | 50 | Target PublishTryDotnetAffectedStoryApi => definition => definition 51 | .Inherit(Build.BaseTarget(ProjectName)) 52 | .DependsOn(CompileTryDotnetAffectedStoryApi, CleanTryDotnetAffectedStoryApi) 53 | .TriggeredBy(c => c.PublishSolution) 54 | .Executes(() => 55 | { 56 | var project = Build.ProjectsToBuild[ProjectName]; 57 | 58 | DotNetTasks.DotNetPublish(s => s 59 | .SetOutput(OutputDirectory) 60 | .SetProject(project) 61 | .SetConfiguration(Build.Configuration) 62 | .EnableNoRestore()); 63 | 64 | Directory.CreateDirectory(Path.GetDirectoryName(PublishArtifact)!); 65 | ZipFile.CreateFromDirectory(OutputDirectory, PublishArtifact); 66 | }); 67 | 68 | Target DeployStackTryDotnetAffectedStoryApi => definition => definition 69 | .After(c => c.DeployStackTryDotnetAffectedDeployBase) 70 | .Before(c => c.ProvisionPulumi) 71 | .DependentFor(c => c.ProvisionPulumi) 72 | .Executes(() => 73 | IPulumiTargets.Stack.With(() => 74 | { 75 | var resourceGroupName = IPulumiTargets.Stack.GetOutput("ResourceGroupName"); 76 | var appService = new WebApp("app", new WebAppArgs 77 | { 78 | ResourceGroupName = resourceGroupName, 79 | Location = ITryDotnetAffectedDeployBase.AzureRegion, 80 | }); 81 | 82 | var publishingCredentials = ListWebAppPublishingCredentials.Invoke(new() 83 | { 84 | ResourceGroupName = resourceGroupName, 85 | Name = appService.Name, 86 | }); 87 | 88 | publishingCredentials.Apply(c => PublishingUsername = c.PublishingUserName); 89 | publishingCredentials.Apply(c => PublishingPassword = c.PublishingPassword); 90 | appService.Name.Apply(c => PublishingAppServiceName = c); 91 | 92 | return new { StoryApiName = appService.Name }; 93 | }) 94 | ); 95 | 96 | Target PublishStackTryDotnetAffectedStoryApi => definition => definition 97 | .Inherit(Build.BaseTarget(ProjectName)) 98 | .DependsOn(DeployStackTryDotnetAffectedStoryApi, PublishTryDotnetAffectedStoryApi) 99 | .DependsOn(c => c.ProvisionPulumi) 100 | .TriggeredBy(c => c.PublishSolution) 101 | .Executes(async () => 102 | { 103 | var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{PublishingUsername}:{PublishingPassword}")); 104 | 105 | await using var package = File.OpenRead(PublishArtifact); 106 | using var httpClient = new HttpClient(); 107 | httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64Auth); 108 | await httpClient.PostAsync($"https://{PublishingAppServiceName}.scm.azurewebsites.net/api/zipdeploy", 109 | new StreamContent(package)); 110 | }); 111 | } -------------------------------------------------------------------------------- /build/Components/Try.Dotnet.Affected.Tests.Build.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.Tools.DotNet; 3 | using Nuke.Common.Tools.MSBuild; 4 | 5 | namespace Components; 6 | 7 | interface ITryDotnetAffectedTestsBuild : INukeBuild 8 | { 9 | const string ProjectName = "Try.Dotnet.Affected.Tests"; 10 | 11 | Target CompileTryDotnetAffectedTests => definition => definition 12 | .Inherit(Build.BaseTarget(ProjectName)) 13 | .DependsOn(c => c.CompileTryDotnetAffected) 14 | .TriggeredBy(c => c.CompileSolution) 15 | .Executes(() => 16 | { 17 | var project = Build.ProjectsToBuild[ProjectName]; 18 | 19 | MSBuildTasks.MSBuild(settings => settings 20 | .SetTargetPath(project) 21 | .SetTargets("Build") 22 | .SetConfiguration(Build.Configuration) 23 | .EnableRestore() 24 | .EnableNodeReuse()); 25 | }); 26 | 27 | Target RunTestsTryDotnetAffectedTests => definition => definition 28 | .Inherit(Build.BaseTarget(ProjectName)) 29 | .DependsOn(CompileTryDotnetAffectedTests) 30 | .TriggeredBy(c => c.RunTestsSolution) 31 | .Executes(() => 32 | { 33 | var project = Build.ProjectsToBuild[ProjectName]; 34 | DotNetTasks.DotNetTest(c => c 35 | .SetConfiguration(Build.Configuration) 36 | .SetProjectFile(project) 37 | .AddLoggers("trx")); 38 | }); 39 | } -------------------------------------------------------------------------------- /build/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Nuke.Common.Tooling; 3 | 4 | [TypeConverter(typeof(TypeConverter))] 5 | public class Configuration : Enumeration 6 | { 7 | public static Configuration Debug = new() { Value = nameof(Debug) }; 8 | public static Configuration Release = new() { Value = nameof(Release) }; 9 | 10 | public static implicit operator string(Configuration configuration) 11 | { 12 | return configuration.Value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /build/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "_build": { 4 | "commandName": "Project", 5 | "workingDirectory": "D:\\source\\repos\\Try.Dotnet.Affected" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | 7 | CS0649;CS0169;CA1050;CA1822;CA2211;IDE1006 8 | .. 9 | .. 10 | 1 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /build/_build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | DO_NOT_SHOW 7 | Implicit 8 | Implicit 9 | ExpressionBody 10 | 0 11 | NEXT_LINE 12 | True 13 | False 14 | 120 15 | IF_OWNER_IS_SINGLE_LINE 16 | WRAP_IF_LONG 17 | False 18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 19 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | True 27 | True 28 | True 29 | -------------------------------------------------------------------------------- /screenshots/BuildTargets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwdb/Try.Dotnet.Affected/1c6999290bc37948081a747026d40f7329ac330a/screenshots/BuildTargets.png --------------------------------------------------------------------------------