diff --git a/README.md b/README.md index c742f7b..e59db5c 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,14 @@ Options: --custom-feed Override URIs to import [] --action Action to perform [default: Download] --about Show credits banner [default: False] - --product-version Override target version to download some product. Advice: Use it with "customFeed". [] + --product-version Override target version to download some product. Advice: Use + it with "customFeed". [] --skip-file-check Skip compare of file sizes if a local file already exists. - Existing file will be skipped to check and redownload. [default: False] + Existing file will be skipped to check and redownload. + [default: False] + --user-agent Set custom user agent via this feature flag. [default: + Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) + Gecko/20100101 Firefox/101.0] --version Show version information -?, -h, --help Show help and usage information ``` diff --git a/src/Core/DownloaderService.cs b/src/Core/DownloaderService.cs index b25a863..42cf0f9 100644 --- a/src/Core/DownloaderService.cs +++ b/src/Core/DownloaderService.cs @@ -16,18 +16,17 @@ using System.Threading.Tasks; internal class DownloaderService : IHostedService { - private readonly string UserAgentString = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0"; - private readonly ILogger logger; private readonly DownloaderOptions options; private readonly HttpClient client; private readonly IHostApplicationLifetime hostApplicationLifetime; + public DownloaderService(IHostApplicationLifetime hostApplicationLifetime, ILogger logger, HttpClient client, DownloaderOptions options) { this.logger = logger; this.client = client; - client.DefaultRequestHeaders.Add("User-Agent", this.UserAgentString); + client.DefaultRequestHeaders.Add("User-Agent", options.UserAgent); this.options = options; this.hostApplicationLifetime = hostApplicationLifetime; } @@ -92,7 +91,7 @@ internal class DownloaderService : IHostedService { var atlassianJson = await this.client.GetStringAsync(feedUrl, cancellationToken).ConfigureAwait(false); var json = atlassianJson.Trim()["downloads(".Length..^1]; - this.logger.LogTrace("Downloaded json: {0}", json); + this.logger.LogTrace($"Downloaded json: {0}", json); var parsed = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true @@ -160,7 +159,7 @@ internal class DownloaderService : IHostedService try { var httpClient = new HttpClient(); - httpClient.DefaultRequestHeaders.Add("User-Agent", this.UserAgentString); + httpClient.DefaultRequestHeaders.Add("User-Agent", options.UserAgent); var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, file.ZipUrl)); if (response.IsSuccessStatusCode) { @@ -238,7 +237,9 @@ internal class DownloaderService : IHostedService this.logger.LogInformation($"File \"{file.ZipUrl}\" successfully downloaded to \"{outputFile}\"."); } + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public async Task StopAsync(CancellationToken cancellationToken) { } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + } diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs new file mode 100644 index 0000000..10b25b1 --- /dev/null +++ b/src/GlobalSuppressions.cs @@ -0,0 +1,12 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:EpicMorg.Atlassian.Downloader.Core.DownloaderService.GetJson(System.String,System.String,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.ValueTuple{System.String,System.Collections.Generic.IDictionary{System.String,EpicMorg.Atlassian.Downloader.ResponseItem[]}}}")] +[assembly: SuppressMessage("Performance", "CA1869:Cache and reuse 'JsonSerializerOptions' instances", Justification = "", Scope = "member", Target = "~M:EpicMorg.Atlassian.Downloader.Core.DownloaderService.GetJson(System.String,System.String,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.ValueTuple{System.String,System.Collections.Generic.IDictionary{System.String,EpicMorg.Atlassian.Downloader.ResponseItem[]}}}")] +[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:EpicMorg.Atlassian.Downloader.Core.DownloaderService.DownloadFilesFromFeed(System.String,System.Collections.Generic.IDictionary{System.String,EpicMorg.Atlassian.Downloader.ResponseItem[]},System.Threading.CancellationToken)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Reliability", "CA2016:Forward the 'CancellationToken' parameter to methods", Justification = "", Scope = "member", Target = "~M:EpicMorg.Atlassian.Downloader.Core.DownloaderService.DownloadFilesFromFeed(System.String,System.Collections.Generic.IDictionary{System.String,EpicMorg.Atlassian.Downloader.ResponseItem[]},System.Threading.CancellationToken)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:EpicMorg.Atlassian.Downloader.Core.DownloaderService.DownloadFile(EpicMorg.Atlassian.Downloader.ResponseItem,System.String,System.Threading.CancellationToken)~System.Threading.Tasks.Task")] diff --git a/src/Models/DownloaderOptions.cs b/src/Models/DownloaderOptions.cs index b606feb..46c36b3 100644 --- a/src/Models/DownloaderOptions.cs +++ b/src/Models/DownloaderOptions.cs @@ -1,4 +1,4 @@ namespace EpicMorg.Atlassian.Downloader; using System; -public record DownloaderOptions(string OutputDir, Uri[] CustomFeed, DownloadAction Action, bool Version, string ProductVersion, bool SkipFileCheck) { } \ No newline at end of file +public record DownloaderOptions(string OutputDir, Uri[] CustomFeed, DownloadAction Action, bool Version, string ProductVersion, bool SkipFileCheck, string UserAgent) { } \ No newline at end of file diff --git a/src/Program.cs b/src/Program.cs index 10c4f23..1746dd4 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -23,7 +23,8 @@ public class Program /// Show credits banner /// Override target version to download some product. Advice: Use it with "customFeed". /// Skip compare of file sizes if a local file already exists. Existing file will be skipped to check and redownload. - static async Task Main(string outputDir, Uri[] customFeed = null, DownloadAction action = DownloadAction.Download, bool about = false, string productVersion = null, bool skipFileCheck = false) => await + /// Set custom user agent via this feature flag. + static async Task Main(string outputDir, Uri[] customFeed = null, DownloadAction action = DownloadAction.Download, bool about = false, string productVersion = null, bool skipFileCheck = false, string userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0") => await Host .CreateDefaultBuilder() .ConfigureHostConfiguration(configHost => configHost.AddEnvironmentVariables()) @@ -45,7 +46,7 @@ public class Program .AddSerilog(dispose: true); }) .AddHostedService() - .AddSingleton(new DownloaderOptions(outputDir, customFeed, action, about, productVersion, skipFileCheck)) + .AddSingleton(new DownloaderOptions(outputDir, customFeed, action, about, productVersion, skipFileCheck, userAgent)) .AddHttpClient()) .RunConsoleAsync() .ConfigureAwait(false);