Created
March 11, 2025 21:11
-
-
Save joperezr/5260cf4d1dddc9c89804e38a67b87ffc to your computer and use it in GitHub Desktop.
Application that helps updating dotnet/aspire repository docker image tag references to latest versions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.AI; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using System.ComponentModel; | |
using System.Data.Common; | |
using System.Text.Json.Serialization; | |
var builder = Host.CreateApplicationBuilder(args); | |
builder.Configuration.AddUserSecrets<Program>(); | |
Uri? ollamaEndpoint = null; | |
string? modelName = null; | |
if (builder.Configuration.GetConnectionString("deepseek") is string connectionString) | |
{ | |
var connectionBuilder = new DbConnectionStringBuilder | |
{ | |
ConnectionString = connectionString | |
}; | |
if (connectionBuilder.ContainsKey("Endpoint") && Uri.TryCreate(connectionBuilder["Endpoint"].ToString(), UriKind.Absolute, out Uri? endpoint)) | |
{ | |
ollamaEndpoint = endpoint; | |
} | |
if (connectionBuilder.ContainsKey("Model")) | |
{ | |
modelName = (string)connectionBuilder["Model"]; | |
} | |
} | |
// Code for GH Copilot | |
// builder.Services.AddSingleton( | |
// new ChatCompletionsClient( | |
// new("https://models.inference.ai.azure.com"), | |
// new AzureKeyCredential(builder.Configuration.GetValue<string>("GH_TOKEN")!))); | |
// builder.Services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Warning)); | |
// builder.Services.AddChatClient(services => services.GetRequiredService<ChatCompletionsClient>().AsChatClient("gpt-4o")) | |
// .UseFunctionInvocation() | |
// .UseLogging(); | |
builder.Services.AddChatClient(new OllamaChatClient(ollamaEndpoint!, modelName!)) | |
.UseLogging(); | |
var app = builder.Build(); | |
var client = app.Services.GetRequiredService<IChatClient>(); | |
string basePrompt = @"You are an expert assistant in software versioning and DevOps. Your task is to analyze a C# source file that contains Docker image tag references and determine if any of these tag references need to be updated. Follow these instructions: | |
1. **Input Parsing:** | |
You are provided with the full contents of a C# file that includes one or more Docker image tag references. For each Docker image reference, identify: | |
- The registry (e.g., ""docker.io""). | |
- The image name (e.g., ""library/mysql"" or ""library/phpmyadmin""). | |
- The tag currently in use (e.g., ""9.1"", ""v4.5"", ""5.2"", or ""latest""). | |
- The associated triple-slash comment line that includes a remarks section indicating the tag value (e.g., ""/// <remarks>9.1</remarks>""). | |
2. **Tag Filtering and Normalization:** | |
- Process tags that contain version information, which may be numeric or prefixed with a character such as 'v' (for example, ""v4.5"" should be treated as version 4.5). | |
- If the tag does not contain any recognizable version information (for example, ""latest""), do not consider it for an update. | |
3. **Fetching Available Tags:** | |
For each Docker image with a version tag, call a provided function that accepts the registry and image name and returns a string listing all available tag names for that image. | |
4. **Version Comparison and Update Decision:** | |
For each image reference with a version tag: | |
- Parse the available tag names using versioning semantics, normalizing tags that start with 'v' by removing the prefix for comparison. | |
- For tags in a two-part format (e.g., ""9.1"" or ""v9.1""), assume that if a newer patch version (like ""9.1.5"") exists, it is already covered by the two-part tag. Do not update in this case. | |
- Update the tag only if there is a clearly newer version with an updated minor version (for example, moving from ""9.1"" to any of ""9.2"", ""9.2.0"", ""9.2.1"", or ""9.2.3"", or from ""v4.5"" to ""v4.7""). | |
- Do not update if the only candidates are prerelease tags (e.g., those containing ""preview"" or similar identifiers). | |
5. **Generating the Updated File:** | |
If any version tag requires updating based on the above rules: | |
- Generate a new version of the file where the tag constants are updated to the latest appropriate two-part version (preserving the original formatting, including any 'v' prefix). | |
- Update the corresponding triple-slash comment line (the remarks section) to match the new tag value. | |
Otherwise, leave the file unchanged. | |
6. **Final Output:** | |
Your final response must be a JSON object with exactly two properties: | |
- ""update_required"": a boolean that is true if any tag updates are needed, or false if all tags are already up-to-date. | |
- ""updated_file"": a string containing the full contents of the C# file with updated tag references and remarks if updates were required. If no updates are needed, return the original file contents. | |
**Important:** | |
- Do not include any text or explanation outside the JSON object. | |
- Ensure the JSON output is valid and contains only the two specified properties. | |
Please analyze the provided file and return your answer as a JSON object according to these instructions."; | |
var chatOptions = new ChatOptions | |
{ | |
Tools = [AIFunctionFactory.Create(GetListOfImageTags)] | |
}; | |
var imageTagFiles = Directory.GetFiles("/home/joperezr/aspire/src", "*ImageTags.cs", SearchOption.AllDirectories); | |
foreach (var file in imageTagFiles) | |
{ | |
try{ | |
var code = File.ReadAllText(file); | |
var update = await client.GetResponseAsync<UpdateResponse>( | |
[ | |
new ChatMessage(Microsoft.Extensions.AI.ChatRole.System, basePrompt), | |
new ChatMessage(Microsoft.Extensions.AI.ChatRole.User, "Help me determine if the docker image tags in this file need to be updated.\n\n```csharp\n" + code + "\n```"), | |
], chatOptions | |
); | |
if (update.Result.UpdateRequired) | |
{ | |
File.WriteAllText(file, update.Result.UpdatedFile); | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine($"Error when parsing file {file}:\n{e.Message}"); | |
} | |
} | |
[Description("Given a registry and image name representing a docker image, returns a string listing all available tag names for that image.")] | |
static string GetListOfImageTags([Description("The registry of the docker image")]string registry, [Description("The image name of the docker image")]string imageName) | |
{ | |
using var process = new System.Diagnostics.Process(); | |
process.StartInfo.FileName = "/home/joperezr/list-tags.sh"; | |
process.StartInfo.Arguments = $"{registry}/{imageName}"; | |
process.StartInfo.UseShellExecute = false; | |
process.StartInfo.RedirectStandardOutput = true; | |
process.Start(); | |
string output = process.StandardOutput.ReadToEnd(); | |
process.WaitForExit(); | |
return output; | |
} | |
public class UpdateResponse | |
{ | |
[JsonPropertyName("update_required")] | |
public bool UpdateRequired { get; set; } | |
[JsonPropertyName("updated_file")] | |
public string UpdatedFile { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment