Skip to content

Commit

Permalink
Merge pull request #43 from EpicMorg/bugfix/fix-output-dir
Browse files Browse the repository at this point in the history
Fix default output dir, enable nullables, fix compiler warnings
  • Loading branch information
stamepicmorg authored Oct 13, 2023
2 parents 997f096 + 00000db commit e81a961
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 46 deletions.
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

0 comments on commit e81a961

Please sign in to comment.