Skip to content

Commit 8a5dce5

Browse files
authored
Add tool for generating the DevConfig file (#3811)
1 parent a00f572 commit 8a5dce5

File tree

7 files changed

+396
-0
lines changed

7 files changed

+396
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.13.35931.197 d17.13
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevConfigGenerator", "DevConfigGenerator\DevConfigGenerator.csproj", "{10ED231D-B95F-452A-887A-401D4012C69B}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{10ED231D-B95F-452A-887A-401D4012C69B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{10ED231D-B95F-452A-887A-401D4012C69B}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{10ED231D-B95F-452A-887A-401D4012C69B}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{10ED231D-B95F-452A-887A-401D4012C69B}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {8EF6C968-097E-41BB-9FF4-DF02A8906310}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Text;
2+
3+
namespace DevConfigGenerator;
4+
5+
public class DevConfigFile
6+
{
7+
public CoreChange? Core { get; set; }
8+
9+
public List<Service> Services { get; } = new List<Service>();
10+
}
11+
12+
public class CoreChange
13+
{
14+
public List<string> Messages { get; } = new List<string>();
15+
16+
public bool UpdateServiceMinimum { get; set; }
17+
18+
public string Type { get; set; } = "patch";
19+
20+
public string FormattedNameColumn()
21+
{
22+
var sb = new StringBuilder();
23+
sb.AppendLine("AWSSDK.Core");
24+
sb.AppendLine($"Update service minimums: [green]{UpdateServiceMinimum}[/]");
25+
return sb.ToString();
26+
}
27+
28+
public string FormattedMessagesColumn()
29+
{
30+
var sb = new StringBuilder();
31+
foreach (var message in Messages)
32+
{
33+
sb.AppendLine(message.ToString());
34+
}
35+
return sb.ToString();
36+
}
37+
}
38+
39+
public class Service
40+
{
41+
public required string Name { get; init; }
42+
43+
public List<string> Messages { get; } = new List<string>();
44+
45+
public string Type { get; set; } = "patch";
46+
47+
48+
public string FormattedNameColumn()
49+
{
50+
var sb = new StringBuilder();
51+
sb.AppendLine($"AWSSDK.{Name}");
52+
return sb.ToString();
53+
}
54+
55+
public string FormattedMessagesColumn()
56+
{
57+
var sb = new StringBuilder();
58+
foreach (var message in Messages)
59+
{
60+
sb.AppendLine(message.ToString());
61+
}
62+
return sb.ToString();
63+
}
64+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="FuzzySharp" Version="2.0.2" />
12+
<PackageReference Include="Spectre.Console" Version="0.50.0" />
13+
<PackageReference Include="Spectre.Console.Cli" Version="0.50.0" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Text.Json;
7+
8+
namespace DevConfigGenerator;
9+
10+
public class DevConfigWriter
11+
{
12+
public static void Write(DevConfigFile config, string filePath)
13+
{
14+
using var stream = File.OpenWrite(filePath);
15+
using Utf8JsonWriter writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
16+
17+
writer.WriteStartObject();
18+
19+
if (config.Core != null)
20+
{
21+
writer.WriteStartObject("core");
22+
23+
writer.WriteBoolean("updateMinimum", config.Core.UpdateServiceMinimum);
24+
ValidateType(config.Core.Type);
25+
writer.WriteString("type", config.Core.Type);
26+
writer.WriteStartArray("changeLogMessages");
27+
foreach (var message in config.Core.Messages)
28+
{
29+
writer.WriteStringValue(message);
30+
}
31+
writer.WriteEndArray();
32+
writer.WriteEndObject();
33+
}
34+
35+
if(config.Services.Any())
36+
{
37+
writer.WriteStartArray("services");
38+
39+
foreach (var service in config.Services)
40+
{
41+
writer.WriteStartObject();
42+
writer.WriteString("serviceName", service.Name);
43+
ValidateType(service.Type);
44+
writer.WriteString("type", service.Type);
45+
46+
writer.WriteStartArray("changeLogMessages");
47+
foreach(var message in service.Messages)
48+
{
49+
writer.WriteStringValue(message);
50+
}
51+
writer.WriteEndArray();
52+
53+
writer.WriteEndObject();
54+
}
55+
56+
writer.WriteEndArray();
57+
}
58+
59+
writer.WriteEndObject();
60+
}
61+
62+
private static void ValidateType(string type)
63+
{
64+
if (!string.Equals("minor", type, StringComparison.Ordinal) && !string.Equals("patch", type, StringComparison.Ordinal))
65+
throw new InvalidDataException($"Change type of {type} is invalid. Valid types are 'minor' and 'patch'");
66+
}
67+
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
using Spectre.Console;
2+
using DevConfigGenerator;
3+
using System.Runtime.InteropServices.Marshalling;
4+
5+
var devConfig = new DevConfigFile();
6+
7+
try
8+
{
9+
while (true)
10+
{
11+
if (devConfig.Core != null || devConfig.Services.Any())
12+
{
13+
var table = new Table();
14+
table.ShowRowSeparators = true;
15+
table.AddColumn("Package");
16+
table.AddColumn("Type");
17+
table.AddColumn("Change Messages");
18+
19+
if (devConfig.Core != null)
20+
{
21+
table.AddRow(devConfig.Core.FormattedNameColumn(), devConfig.Core.Type, devConfig.Core.FormattedMessagesColumn());
22+
}
23+
24+
foreach (var service in devConfig.Services)
25+
{
26+
table.AddRow(service.FormattedNameColumn(), service.Type, service.FormattedMessagesColumn());
27+
}
28+
29+
AnsiConsole.Write(table);
30+
}
31+
32+
var changeType = AnsiConsole.Prompt(
33+
new SelectionPrompt<string>()
34+
.Title("What type of change was made?")
35+
.AddChoices(new[] {
36+
"Core", "Generator", "Service", "Save DevConfigFile", "Exit Without Saving"
37+
}));
38+
39+
switch (changeType)
40+
{
41+
case "Core":
42+
CoreChange();
43+
break;
44+
case "Generator":
45+
GeneratorChange();
46+
break;
47+
case "Service":
48+
ServiceChange();
49+
break;
50+
case "Save DevConfigFile":
51+
WriteDevConfigFile();
52+
return;
53+
case "Exit Without Saving":
54+
return;
55+
}
56+
}
57+
58+
}
59+
catch (Exception e)
60+
{
61+
Console.WriteLine("Unhandled exception while generating dev config file");
62+
Console.WriteLine(e.Message);
63+
}
64+
65+
void WriteDevConfigFile()
66+
{
67+
if (devConfig.Core == null && !devConfig.Services.Any())
68+
{
69+
AnsiConsole.Markup("[yellow]Skipped writing dev config since the config is empty[/]\n");
70+
}
71+
72+
var devConfigDirectory = Path.Combine(FindRepoRoot(), "generator", ".DevConfigs");
73+
if (!Directory.Exists(devConfigDirectory))
74+
{
75+
Directory.CreateDirectory(devConfigDirectory);
76+
}
77+
78+
var devConfigFilePath = Path.Combine(devConfigDirectory, Guid.NewGuid().ToString() + ".json");
79+
DevConfigWriter.Write(devConfig, devConfigFilePath);
80+
81+
AnsiConsole.Markup($"DevConfig file written to: [green]{devConfigFilePath}[/]\n");
82+
AnsiConsole.WriteLine(File.ReadAllText(devConfigFilePath));
83+
}
84+
85+
86+
void CoreChange()
87+
{
88+
var change = AnsiConsole.Prompt(new TextPrompt<string>("Enter the change message for the [green]core[/] change:"));
89+
90+
var newCoreConfig = false;
91+
if (devConfig.Core == null)
92+
{
93+
newCoreConfig = true;
94+
devConfig.Core = new CoreChange();
95+
}
96+
97+
devConfig.Core.Messages.Add(change);
98+
99+
devConfig.Core.Type = AskChangeType(devConfig.Core.Type);
100+
101+
var textPrompt = new TextPrompt<bool>("Should service packages be updated to use new Core?")
102+
.AddChoice(true)
103+
.AddChoice(false)
104+
.WithConverter(choice => choice ? "y" : "n");
105+
106+
// If added a second change message to core then default the update service minimum to the value choosen for the previous entry.
107+
if (!newCoreConfig)
108+
{
109+
textPrompt.DefaultValue(devConfig.Core.UpdateServiceMinimum);
110+
}
111+
112+
var updateServiceMinimum = AnsiConsole.Prompt(textPrompt);
113+
114+
devConfig.Core.UpdateServiceMinimum = updateServiceMinimum;
115+
}
116+
117+
void GeneratorChange()
118+
{
119+
AnsiConsole.Markup("Generator changes are saved as [green]Core changes[/] with update service minimum set to [green]true[/].\n");
120+
var change = AnsiConsole.Prompt(new TextPrompt<string>("Enter the change message for the [green]generator[/] change:"));
121+
122+
if (devConfig.Core == null)
123+
{
124+
devConfig.Core = new CoreChange();
125+
}
126+
127+
devConfig.Core.Messages.Add(change);
128+
devConfig.Core.UpdateServiceMinimum = true;
129+
130+
devConfig.Core.Type = AskChangeType(devConfig.Core.Type);
131+
}
132+
133+
void ServiceChange()
134+
{
135+
var serviceNames = new List<string>();
136+
foreach(var serviceDirectory in Directory.GetDirectories(Path.Combine(FindRepoRoot(), "sdk", "src", "Services")))
137+
{
138+
serviceNames.Add(new DirectoryInfo(serviceDirectory).Name);
139+
}
140+
141+
var searchQuery = AnsiConsole.Ask<string>("What service was changed (The name should match directory name in the sdk/src/Services parent directory)?");
142+
143+
144+
string serviceName;
145+
if (serviceNames.FirstOrDefault(x => string.Equals(x, searchQuery, StringComparison.OrdinalIgnoreCase)) != null)
146+
{
147+
serviceName = serviceNames.FirstOrDefault(x => string.Equals(x, searchQuery, StringComparison.OrdinalIgnoreCase))!;
148+
}
149+
else
150+
{
151+
var topMatches = FuzzySharp.Process.ExtractTop(searchQuery, serviceNames)
152+
.Select(result => result.Value)
153+
.ToList();
154+
155+
serviceName = AnsiConsole.Prompt(
156+
new SelectionPrompt<string>()
157+
.PageSize(10)
158+
.AddChoices(topMatches));
159+
}
160+
161+
162+
var service = devConfig.Services.FirstOrDefault(x => x.Name == serviceName);
163+
164+
// Check to see if this is the first change log entry for this service being added to the dev config.
165+
// If so add the service to the list of services being changed.
166+
if (service == null)
167+
{
168+
service = new Service { Name = serviceName };
169+
devConfig.Services.Add(service);
170+
}
171+
172+
var change = AnsiConsole.Prompt(new TextPrompt<string>($"Enter the change message for [green]{serviceName}[/] change:"));
173+
service.Messages.Add(change);
174+
175+
service.Type = AskChangeType(service.Type);
176+
}
177+
178+
static string AskChangeType(string defaultValue)
179+
{
180+
string[] choices;
181+
if (defaultValue == "patch")
182+
{
183+
choices = new[] { "patch", "minor" };
184+
}
185+
else
186+
{
187+
choices = new[] { "minor", "patch" };
188+
}
189+
190+
var changeType = AnsiConsole.Prompt(
191+
new SelectionPrompt<string>()
192+
.Title("Type of change?")
193+
.AddChoices(choices));
194+
195+
return changeType;
196+
}
197+
198+
static string FindRepoRoot()
199+
{
200+
var directory = Directory.GetParent(typeof(Program).Assembly.Location)?.FullName;
201+
while(directory != null)
202+
{
203+
if (File.Exists(Path.Combine(directory, "SDK.CHANGELOG.MD")))
204+
{
205+
return directory;
206+
}
207+
208+
directory = Directory.GetParent(directory)?.FullName;
209+
}
210+
211+
throw new ApplicationException("Failed to resolve SDK's repository root. The tool must be run within the SDK's repository");
212+
}

buildtools/add-devconfig.bat

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
@echo off
3+
cd /d "%~dp0"
4+
5+
dotnet build ./DevConfigGenerator/DevConfigGenerator/DevConfigGenerator.csproj
6+
dotnet exec ./DevConfigGenerator/DevConfigGenerator/bin/Debug/net8.0/DevConfigGenerator.dll

0 commit comments

Comments
 (0)