-
Notifications
You must be signed in to change notification settings - Fork 153
Expand file tree
/
Copy pathcheck-version.cs
More file actions
164 lines (139 loc) · 5.12 KB
/
Copy pathcheck-version.cs
File metadata and controls
164 lines (139 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using System.Text.Json;
internal static class Program
{
private const string MarketplaceName = "power-platform-skills";
private const string DefaultManifestUrl =
"https://raw.githubusercontent.com/microsoft/power-platform-skills/main/plugins/canvas-apps/.plugin/plugin.json";
private static async Task<int> Main(string[] args)
{
try
{
return await CheckVersion(args);
}
catch (Exception)
{
// Version discovery is advisory and must never block MCP configuration.
return 0;
}
}
private static async Task<int> CheckVersion(string[] args)
{
string pluginRoot = GetRequiredOption(args, "--plugin-root");
string manifestUrl = GetOption(args, "--manifest-url") ?? DefaultManifestUrl;
PluginManifest local = ReadManifest(
Path.Combine(pluginRoot, ".plugin", "plugin.json")
);
using var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(5),
};
string remoteJson = await httpClient.GetStringAsync(manifestUrl);
PluginManifest remote = ParseManifest(remoteJson);
if (CompareVersions(local.Version, remote.Version) >= 0)
{
return 0;
}
Console.WriteLine(
$"Plugin update available: {local.Name} {local.Version} -> {remote.Version}."
);
WriteUpdateCommands(local.Name);
return 0;
}
private static void WriteUpdateCommands(string pluginName)
{
string? cli = DetectPluginCli();
if (cli is not null)
{
Console.WriteLine("Run:");
WriteUpdateCommands(cli, pluginName, " ");
return;
}
Console.WriteLine("Run the commands for your CLI:");
Console.WriteLine(" Claude Code:");
WriteUpdateCommands("claude", pluginName, " ");
Console.WriteLine(" GitHub Copilot CLI:");
WriteUpdateCommands("copilot", pluginName, " ");
}
private static void WriteUpdateCommands(string cli, string pluginName, string indent)
{
Console.WriteLine($"{indent}{cli} plugin marketplace update {MarketplaceName}");
Console.WriteLine(
$"{indent}{cli} plugin update {pluginName}@{MarketplaceName}"
);
}
private static string? DetectPluginCli()
{
// Match the host detection contract used by shared telemetry. Claude
// wins if both markers are present, which avoids ambiguous instructions.
if (IsTruthyEnvironmentVariable("CLAUDECODE"))
{
return "claude";
}
return IsTruthyEnvironmentVariable("COPILOT_CLI") ? "copilot" : null;
}
private static bool IsTruthyEnvironmentVariable(string name)
{
string? value = Environment.GetEnvironmentVariable(name);
if (value is null)
{
return false;
}
string normalized = value.Trim().ToLowerInvariant();
return normalized is not ("" or "0" or "false");
}
private static PluginManifest ReadManifest(string path) =>
ParseManifest(File.ReadAllText(path));
private static PluginManifest ParseManifest(string json)
{
using JsonDocument document = JsonDocument.Parse(json);
JsonElement root = document.RootElement;
return new PluginManifest(
root.GetProperty("name").GetString()
?? throw new InvalidDataException("Plugin name is missing."),
root.GetProperty("version").GetString()
?? throw new InvalidDataException("Plugin version is missing.")
);
}
private static string GetRequiredOption(string[] args, string option) =>
GetOption(args, option)
?? throw new ArgumentException($"Missing required option: {option}");
private static string? GetOption(string[] args, string option)
{
for (int index = 0; index < args.Length - 1; index++)
{
if (args[index] == option)
{
return args[index + 1];
}
}
return null;
}
private static int CompareVersions(string left, string right)
{
string[] leftSegments = left.Split('.');
string[] rightSegments = right.Split('.');
int segmentCount = Math.Max(leftSegments.Length, rightSegments.Length);
for (int index = 0; index < segmentCount; index++)
{
int leftSegment = ParseSegment(leftSegments, index, left);
int rightSegment = ParseSegment(rightSegments, index, right);
int comparison = leftSegment.CompareTo(rightSegment);
if (comparison != 0)
{
return comparison;
}
}
return 0;
}
private static int ParseSegment(string[] segments, int index, string version)
{
if (index >= segments.Length)
{
return 0;
}
return int.TryParse(segments[index], out int value)
? value
: throw new FormatException($"Invalid plugin version: {version}");
}
private sealed record PluginManifest(string Name, string Version);
}