Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix default output dir, enable nullables, fix compiler warnings #43

Merged
merged 1 commit into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/Core/BellsAndWhistles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ internal class BellsAndWhistles
{
private static readonly string assemblyEnvironment = string.Format("[{1}, {0}]", RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(), RuntimeInformation.FrameworkDescription);

private static readonly Assembly entryAssembly = Assembly.GetEntryAssembly();
private static readonly Assembly entryAssembly = Assembly.GetEntryAssembly()!;

private static readonly string assemblyVersion = entryAssembly.GetName().Version.ToString();
private static readonly string assemblyVersion = entryAssembly.GetName().Version!.ToString();

private static readonly string fileVersion = entryAssembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
private static readonly string fileVersion = entryAssembly.GetCustomAttribute<AssemblyFileVersionAttribute>()!.Version;

private static readonly string assemblyName = entryAssembly.GetCustomAttribute<AssemblyProductAttribute>().Product;
private static readonly string assemblyName = entryAssembly.GetCustomAttribute<AssemblyProductAttribute>()!.Product;
const string assemblyBuildType =
#if DEBUG
"[Debug]"
Expand All @@ -31,7 +31,12 @@ internal class BellsAndWhistles

public static void ShowVersionInfo(ILogger logger)
{
logger.LogInformation($"{assemblyName} {assemblyVersion} {assemblyEnvironment} {assemblyBuildType}");
logger.LogInformation(
"{assemblyName} {assemblyVersion} {assemblyEnvironment} {assemblyBuildType}",
assemblyName,
assemblyVersion,
assemblyEnvironment,
assemblyBuildType);
Console.BackgroundColor = ConsoleColor.Black;
WriteColorLine("%╔═╦═══════════════════════════════════════════════════════════════════════════════════════╦═╗");
WriteColorLine("%╠═╝ .''. %╚═%╣");
Expand Down
54 changes: 28 additions & 26 deletions src/Core/DownloaderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ namespace EpicMorg.Atlassian.Downloader.Core;

internal class DownloaderService : IHostedService
{
private static readonly JsonSerializerOptions jsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
private readonly ILogger<DownloaderService> logger;
private readonly DownloaderOptions options;
private readonly HttpClient client;
private readonly IHostApplicationLifetime hostApplicationLifetime;


public DownloaderService(IHostApplicationLifetime hostApplicationLifetime, ILogger<DownloaderService> logger, HttpClient client, DownloaderOptions options)
{
this.logger = logger;
Expand Down Expand Up @@ -87,15 +90,12 @@ public async Task StartAsync(CancellationToken cancellationToken)
this.hostApplicationLifetime.StopApplication();
}

private async Task<(string json, IDictionary<string, ResponseItem[]> versions)> GetJson(string feedUrl, string productVersion = null, CancellationToken cancellationToken = default)
private async Task<(string json, IDictionary<string, ResponseItem[]> versions)> GetJson(string feedUrl, string? productVersion = null, CancellationToken cancellationToken = default)
{
var atlassianJson = await this.client.GetStringAsync(feedUrl, cancellationToken).ConfigureAwait(false);
var json = atlassianJson.Trim()["downloads(".Length..^1];
this.logger.LogTrace($"Downloaded json: {0}", json);
var parsed = JsonSerializer.Deserialize<ResponseItem[]>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
this.logger.LogTrace("Downloaded json: {json}", json);
var parsed = JsonSerializer.Deserialize<ResponseItem[]>(json, jsonOptions)!;
this.logger.LogDebug("Found {releaseCount} releases", parsed.Length);
var versions = parsed
.GroupBy(a => a.Version)
Expand All @@ -112,7 +112,6 @@ private IReadOnlyList<string> GetFeedUrls() => this.options.CustomFeed != null
private async Task DownloadFilesFromFeed(string feedUrl, IDictionary<string, ResponseItem[]> versions, CancellationToken cancellationToken)
{


var feedDir = Path.Combine(this.options.OutputDir, feedUrl[(feedUrl.LastIndexOf('/') + 1)..feedUrl.LastIndexOf('.')]);
this.logger.LogInformation("Download from JSON \"{feedUrl}\" started", feedUrl);
foreach (var version in versions)
Expand All @@ -137,12 +136,10 @@ private async Task DownloadFilesFromFeed(string feedUrl, IDictionary<string, Res

if (file.ZipUrl == null)
{
this.logger.LogWarning($"Empty ZipUrl found for version '{version.Key}' in {feedUrl}");
this.logger.LogWarning("Empty ZipUrl found for version '{version}' in {feedUrl}", version.Key, feedUrl);
continue;
}



var serverPath = file.ZipUrl.PathAndQuery;
var outputFile = Path.Combine(directory, serverPath[(serverPath.LastIndexOf('/') + 1)..]);
if (!File.Exists(outputFile))
Expand All @@ -153,58 +150,64 @@ private async Task DownloadFilesFromFeed(string feedUrl, IDictionary<string, Res
{
if (this.options.SkipFileCheck == false)
{
this.logger.LogWarning($"File \"{outputFile}\" already exists. File sizes will be compared.");
this.logger.LogWarning("File \"{outputFile}\" already exists. File sizes will be compared.", outputFile);
var localFileSize = new FileInfo(outputFile).Length;
this.logger.LogInformation($"Size of local file is {localFileSize} bytes.");
this.logger.LogInformation("Size of local file is {localFileSize} bytes.", localFileSize);
try
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", options.UserAgent);
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, file.ZipUrl));
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, file.ZipUrl), cancellationToken);
if (response.IsSuccessStatusCode)
{
if (response.Content.Headers.ContentLength.HasValue)
{
var remoteFileSize = response.Content.Headers.ContentLength.Value;
this.logger.LogInformation($"Size of remote file is \"{remoteFileSize}\" bytes.");
this.logger.LogInformation("Size of remote file is \"{remoteFileSize}\" bytes.", remoteFileSize);

if (remoteFileSize == localFileSize)
{
this.logger.LogInformation($"Size of remote and local files and are same ({remoteFileSize} bytes and {localFileSize} bytes). Nothing to download. Operation skipped.");
this.logger.LogInformation(
"Size of remote and local files and are same ({remoteFileSize} bytes and {localFileSize} bytes). Nothing to download. Operation skipped.",
remoteFileSize,
localFileSize);
}
else
{
this.logger.LogWarning($"Size of remote and local files and are not same ({remoteFileSize} bytes and {localFileSize} bytes). Download started.");
this.logger.LogWarning(
"Size of remote and local files and are not same ({remoteFileSize} bytes and {localFileSize} bytes). Download started.",
remoteFileSize,
localFileSize);
File.Delete(outputFile);
await this.DownloadFile(file, outputFile, cancellationToken).ConfigureAwait(false);
}
}
else
{
this.logger.LogWarning($"Cant get size of remote file \"{file.ZipUrl}\". May be server not support it feature. Sorry.");
this.logger.LogWarning("Cant get size of remote file \"{uri}\". May be server not support it feature. Sorry.", file.ZipUrl);
continue;
}
}
else
{
this.logger.LogCritical($"Request execution error: \"{response.StatusCode}\". Sorry.");
this.logger.LogError("Request execution error for {uri}: \"{statusCode}\". Sorry.", file.ZipUrl, response.StatusCode);
}
}
catch (HttpRequestException ex)
{
this.logger.LogCritical($"HTTP request error: \"{ex.Message}\", \"{ex.StackTrace}\", \"{ex.StatusCode}\". Sorry.");
}
this.logger.LogError(ex, "HTTP request error for {uri}: \"{message}\", \"{statusCode}\". Sorry.", file.ZipUrl, ex.Message, ex.StatusCode);
}
}
else
{
logger.LogWarning($"File \"{outputFile}\" already exists. Download from \"{file.ZipUrl}\" skipped.");
logger.LogWarning("File \"{outputFile}\" already exists. Download from \"{uri}\" skipped.", outputFile, file.ZipUrl);
continue;
}
}
}
}

this.logger.LogInformation($"All files from \"{feedUrl}\" successfully downloaded.");
this.logger.LogInformation("All files from \"{feedUrl}\" successfully downloaded.", feedUrl);

}

Expand All @@ -230,14 +233,13 @@ private async Task DownloadFile(ResponseItem file, string outputFile, Cancellati
}
catch (Exception removeEx)
{
this.logger.LogError(removeEx, $"Failed to remove incomplete file \"{outputFile}\".");
this.logger.LogError(removeEx, "Failed to remove incomplete file \"{outputFile}\".", outputFile);
}
}

this.logger.LogInformation($"File \"{file.ZipUrl}\" successfully downloaded to \"{outputFile}\".");
this.logger.LogInformation("File \"{uri}\" successfully downloaded to \"{outputFile}\".", file.ZipUrl, 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
Expand Down
9 changes: 8 additions & 1 deletion src/Models/DownloaderOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
namespace EpicMorg.Atlassian.Downloader;
using System;

public record DownloaderOptions(string OutputDir, Uri[] CustomFeed, DownloadAction Action, bool Version, string ProductVersion, bool SkipFileCheck, string UserAgent) { }
public record DownloaderOptions(
string OutputDir,
Uri[]? CustomFeed,
DownloadAction Action,
bool Version,
string? ProductVersion,
bool SkipFileCheck,
string UserAgent) { }
24 changes: 12 additions & 12 deletions src/Models/ResponseItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

public partial class ResponseItem
{
public string Description { get; set; }
public string Edition { get; set; }
public Uri ZipUrl { get; set; }
public object TarUrl { get; set; }
public string Md5 { get; set; }
public string Size { get; set; }
public string Released { get; set; }
public string Type { get; set; }
public string Platform { get; set; }
public string Version { get; set; }
public Uri ReleaseNotes { get; set; }
public Uri UpgradeNotes { get; set; }
public string? Description { get; set; }
public string? Edition { get; set; }
public Uri? ZipUrl { get; set; }
public object? TarUrl { get; set; }
public string? Md5 { get; set; }
public string? Size { get; set; }
public string? Released { get; set; }
public string? Type { get; set; }
public string? Platform { get; set; }
public required string Version { get; set; }
public Uri? ReleaseNotes { get; set; }
public Uri? UpgradeNotes { get; set; }
}
18 changes: 16 additions & 2 deletions src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ public class Program
/// <param name="productVersion">Override target version to download some product. Advice: Use it with "customFeed".</param>
/// <param name="skipFileCheck">Skip compare of file sizes if a local file already exists. Existing file will be skipped to check and redownload.</param>
/// <param name="userAgent">Set custom user agent via this feature flag.</param>
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
static async Task Main(
string? outputDir = default,
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())
Expand All @@ -46,7 +53,14 @@ public class Program
.AddSerilog(dispose: true);
})
.AddHostedService<DownloaderService>()
.AddSingleton(new DownloaderOptions(outputDir, customFeed, action, about, productVersion, skipFileCheck, userAgent))
.AddSingleton(new DownloaderOptions(
outputDir ?? Environment.CurrentDirectory,
customFeed,
action,
about,
productVersion,
skipFileCheck,
userAgent))
.AddHttpClient())
.RunConsoleAsync()
.ConfigureAwait(false);
Expand Down
1 change: 1 addition & 0 deletions src/atlassian-downloader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier>

<!--build props-->
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
Expand Down
Loading