Skip to content

Commit 0eb0036

Browse files
Update VersionHelper to utilize GH release tag for version check (#4224)
* part II versionhelper.cs changes * correction * indentation * GitHubReleaseApiUrl changes * corrections * suggestion --------- Co-authored-by: liliankasem <[email protected]>
1 parent 2619642 commit 0eb0036

File tree

3 files changed

+80
-108
lines changed

3 files changed

+80
-108
lines changed

Diff for: src/Azure.Functions.Cli/Common/Constants.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ internal static class Constants
6969
public const int DefaultGetFunctionReadinessTime = 30000;
7070
public const int DefaultRestartedWorkerProcessUptimeWithin = 45000;
7171
public const string HelpCommand = "help";
72-
public const string CoreToolsVersionsFeedUrl = "https://functionscdn.azureedge.net/public/cli-feed-v4.json";
7372
public const string OldCoreToolsVersionMessage = "You are using an old Core Tools version. Please upgrade to the latest version.";
7473
public const string GetFunctionNameParamId = "trigger-functionName";
7574
public const string HttpTriggerAuthLevelParamId = "httpTrigger-authLevel";
@@ -95,6 +94,8 @@ internal static class Constants
9594
public const string InProcDotNet8EnabledSetting = "FUNCTIONS_INPROC_NET8_ENABLED";
9695
public const string AzureDevSessionsRemoteHostName = "AzureDevSessionsRemoteHostName";
9796
public const string AzureDevSessionsPortSuffixPlaceholder = "<port>";
97+
public const string GitHubReleaseApiUrl = "https://api.github.com/repos/Azure/azure-functions-core-tools/releases/latest";
98+
9899
// Sample format https://n12abc3t-<port>.asse.devtunnels.ms/
99100

100101

Diff for: src/Azure.Functions.Cli/Helpers/VersionHelper.cs

+17-107
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Azure.Functions.Cli.Common;
2+
using Azure.Functions.Cli.Extensions;
23
using Colors.Net;
34
using Newtonsoft.Json;
45
using Newtonsoft.Json.Serialization;
@@ -7,6 +8,7 @@
78
using System.Collections.Generic;
89
using System.IO;
910
using System.Linq;
11+
using System.Net.Http;
1012
using System.Reflection;
1113
using System.Runtime.InteropServices;
1214
using System.Text;
@@ -17,6 +19,9 @@ namespace Azure.Functions.Cli.Helpers
1719
{
1820
internal class VersionHelper
1921
{
22+
// Set the CliVersion for testing
23+
internal static string CliVersion { get; set; } = Constants.CliVersion;
24+
2025
public static async Task<string> RunAsync(Task<bool> isRunningOlderVersion = null)
2126
{
2227
isRunningOlderVersion ??= IsRunningAnOlderVersion();
@@ -36,39 +41,25 @@ public static async Task<string> RunAsync(Task<bool> isRunningOlderVersion = nul
3641
// Check that current core tools is the latest version.
3742
// To ensure that it doesn't block other tasks. The HTTP Request timeout is only 1 second.
3843
// We simply ingnore the exception if for any reason the check fails.
39-
public static async Task<bool> IsRunningAnOlderVersion()
44+
public static async Task<bool> IsRunningAnOlderVersion(HttpClient client = null)
4045
{
4146
try
4247
{
43-
var client = new System.Net.Http.HttpClient
44-
{
45-
Timeout = TimeSpan.FromSeconds(1)
46-
};
47-
var response = await client.GetAsync(Constants.CoreToolsVersionsFeedUrl);
48-
var content = await response.Content.ReadAsStringAsync();
49-
var data = JsonConvert.DeserializeObject<CliFeed>(content);
50-
IEnumerable releases = ((IEnumerable) data.Releases);
51-
var releaseList = new List<ReleaseSummary>();
52-
foreach (var item in releases)
48+
using (client ??= new HttpClient())
5349
{
54-
var jProperty = (Newtonsoft.Json.Linq.JProperty)item;
55-
var releaseDetail = JsonConvert.DeserializeObject<ReleaseDetail>(jProperty.Value.ToString());
56-
releaseList.Add(new ReleaseSummary() { Release = jProperty.Name, ReleaseDetail = releaseDetail.ReleaseList.FirstOrDefault() });
57-
}
50+
client.DefaultRequestHeaders.Add("User-Agent", "AzureFunctionsCoreToolsClient");
5851

59-
var latestCoreToolsReleaseVersion = releaseList.FirstOrDefault(x => x.Release == data.Tags.V4Release.ReleaseVersion)?.CoreToolsReleaseNumber;
52+
var response = await client.GetAsync(Constants.GitHubReleaseApiUrl);
53+
response.EnsureSuccessStatusCode();
6054

61-
if (!string.IsNullOrEmpty(latestCoreToolsReleaseVersion) &&
62-
Constants.CliVersion != latestCoreToolsReleaseVersion)
63-
{
64-
return true;
65-
}
55+
var content = await response.Content.ReadAsStringAsync();
56+
var release = JsonConvert.DeserializeObject<GitHubRelease>(content);
6657

67-
return false;
58+
return !release.TagName.EqualsIgnoreCase(CliVersion);
59+
}
6860
}
6961
catch (Exception)
7062
{
71-
// ignore exception and no warning when the check fails.
7263
return false;
7364
}
7465
}
@@ -123,91 +114,10 @@ private static async Task<string> GetMultipleInstallationMessage(bool isRunningO
123114
return string.Empty;
124115
}
125116

126-
private class CliFeed
127-
{
128-
[JsonProperty("tags")]
129-
public Tags Tags { get; set; }
130-
131-
[JsonProperty("releases")]
132-
public Object Releases { get; set; }
133-
}
134-
135-
private class Tags
136-
{
137-
[JsonProperty("v4")]
138-
public Release V4Release { get; set; }
139-
140-
[JsonProperty("v4-prerelease")]
141-
public Release V4PreRelease { get; set; }
142-
143-
[JsonProperty("v3")]
144-
public Release V3Release { get; set; }
145-
146-
[JsonProperty("v3-prerelease")]
147-
public Release V3PreRelease { get; set; }
148-
}
149-
150-
private class Release
151-
{
152-
[JsonProperty("release")]
153-
public string ReleaseVersion { get; set; }
154-
155-
[JsonProperty("releaseQuality")]
156-
public string ReleaseQuality { get; set; }
157-
158-
[JsonProperty("hidden")]
159-
public bool Hidden { get; set; }
160-
}
161-
162-
private class ReleaseSummary
117+
private class GitHubRelease
163118
{
164-
public string Release { get; set; }
165-
166-
public string CoreToolsReleaseNumber
167-
{
168-
get
169-
{
170-
var downloadLink = ReleaseDetail?.DownloadLink;
171-
if (string.IsNullOrEmpty(ReleaseDetail?.DownloadLink))
172-
{
173-
return string.Empty;
174-
}
175-
176-
Uri uri = new UriBuilder(ReleaseDetail?.DownloadLink).Uri;
177-
178-
if (uri.Segments.Length < 4)
179-
{
180-
return string.Empty;
181-
}
182-
183-
return uri.Segments[2].Replace("/", string.Empty);
184-
}
185-
}
186-
public CoreToolsRelease ReleaseDetail { get; set; }
187-
}
188-
189-
private class ReleaseDetail
190-
{
191-
[JsonProperty("coreTools")]
192-
public IList<CoreToolsRelease> ReleaseList { get; set; }
193-
}
194-
195-
private class CoreToolsRelease
196-
{
197-
[JsonProperty("OS")]
198-
public string Os { get; set; }
199-
200-
[JsonProperty("Architecture")]
201-
public string Architecture { get; set; }
202-
203-
[JsonProperty("downloadLink")]
204-
public string DownloadLink { get; set; }
205-
206-
[JsonProperty("size")]
207-
public string Size { get; set; }
208-
209-
[JsonProperty("default")]
210-
public bool Default { get; set; }
119+
[JsonProperty("tag_name")]
120+
public string TagName { get; set; }
211121
}
212122
}
213123

Diff for: test/Azure.Functions.Cli.Tests/E2E/VersionTests.cs

+61
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
using System;
2+
using System.Net.Http;
3+
using System.Net;
4+
using System.Threading;
25
using System.Threading.Tasks;
36
using Azure.Functions.Cli.Common;
7+
using Azure.Functions.Cli.Helpers;
48
using Azure.Functions.Cli.Tests.E2E.Helpers;
9+
using Moq.Protected;
10+
using Moq;
511
using Xunit;
612
using Xunit.Abstractions;
13+
using static Azure.Functions.Cli.Helpers.VersionHelper;
14+
using FluentAssertions;
715

816
namespace Azure.Functions.Cli.Tests.E2E
917
{
@@ -24,5 +32,58 @@ await CliTester.Run(new RunConfiguration
2432
CommandTimeout = TimeSpan.FromSeconds(30)
2533
}, _output);
2634
}
35+
36+
[Fact]
37+
public async Task IsRunningAnOlderVersion_ShouldReturnTrue_WhenVersionIsOlder()
38+
{
39+
// Create the mocked HttpClient with the mock response
40+
var mockHttpClient = GetMockHttpClientWithResponse();
41+
42+
VersionHelper.CliVersion = "4.0.5";
43+
var result = await VersionHelper.IsRunningAnOlderVersion(mockHttpClient);
44+
45+
result.Should().Be(true);
46+
}
47+
48+
[Fact]
49+
public async Task IsRunningAnOlderVersion_ShouldReturnFalse_WhenVersionIsUpToDate()
50+
{
51+
// Create the mocked HttpClient with the mock response
52+
var mockHttpClient = GetMockHttpClientWithResponse();
53+
54+
VersionHelper.CliVersion = "4.0.6610";
55+
var result = await VersionHelper.IsRunningAnOlderVersion(mockHttpClient);
56+
57+
result.Should().Be(false);
58+
}
59+
60+
// Method to return a mocked HttpClient
61+
private HttpClient GetMockHttpClientWithResponse()
62+
{
63+
var mockJsonResponse = @"{
64+
'tag_name':'4.0.6610',
65+
'target_commitish': '48490a7ee744ed435fdce62f5e1f2f39c61c5309',
66+
'name': '4.0.6610',
67+
'draft': false,
68+
'prerelease': false,
69+
'created_at': '',
70+
'published_at': '2024-11-13T22:08:49Z',
71+
}";
72+
var mockHandler = new Mock<HttpMessageHandler>();
73+
74+
// Mock the SendAsync method to return a mocked response
75+
mockHandler.Protected()
76+
.Setup<Task<HttpResponseMessage>>("SendAsync",
77+
ItExpr.IsAny<HttpRequestMessage>(),
78+
ItExpr.IsAny<CancellationToken>())
79+
.ReturnsAsync(new HttpResponseMessage
80+
{
81+
StatusCode = HttpStatusCode.OK,
82+
Content = new StringContent(mockJsonResponse)
83+
});
84+
85+
// Return HttpClient with mocked handler
86+
return new HttpClient(mockHandler.Object);
87+
}
2788
}
2889
}

0 commit comments

Comments
 (0)