Skip to content

Commit

Permalink
Merge pull request #427 from WildernessLabs/download_manager
Browse files Browse the repository at this point in the history
Download manager simplification
  • Loading branch information
ctacke authored Jan 17, 2024
2 parents f5f8d47 + 7c6172f commit db2fdd7
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override async ValueTask ExecuteCommand()

if (latest == null)
{
Logger?.LogError($"Unable to get latest version information.");
Logger?.LogError($"Unable to get latest version information");
return;
}

Expand Down Expand Up @@ -65,11 +65,11 @@ protected override async ValueTask ExecuteCommand()

if (!result)
{
Logger?.LogError($"Unable to download package '{Version}'.");
Logger?.LogError($"Unable to download package '{Version}'");
}
else
{
Logger?.LogError($"{Environment.NewLine} Firmware package '{Version}' downloaded.");
Logger?.LogInformation($"Firmware package '{Version}' downloaded");

if (explicitVersion == false)
{
Expand Down
203 changes: 68 additions & 135 deletions Source/v2/Meadow.Hcom/Firmware/DownloadManager.cs
Original file line number Diff line number Diff line change
@@ -1,72 +1,55 @@
using Microsoft.Extensions.Logging;
using System.IO.Compression;
using System.Reflection;
using System.Text.Json;

namespace Meadow.Hcom;

public class DownloadManager
{
public static readonly string FirmwareDownloadsFilePathRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"WildernessLabs",
"Firmware");

public static string FirmwareLatestVersion
{
get
{
string latest_txt = Path.Combine(FirmwareDownloadsFilePathRoot, "latest.txt");
if (File.Exists(latest_txt))
return File.ReadAllText(latest_txt);
else
throw new FileNotFoundException("OS download was not found.");
}
}

public static string FirmwareDownloadsFilePath => FirmwarePathForVersion(FirmwareLatestVersion);

public static string FirmwarePathForVersion(string firmwareVersion)
{
return Path.Combine(FirmwareDownloadsFilePathRoot, firmwareVersion);
}

public static readonly string WildernessLabsTemp = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"WildernessLabs",
"temp");
static readonly string RootFolder = "WildernessLabs";
static readonly string FirmwareFolder = "Firmware";
static readonly string LatestFilename = "latest.txt";

public static readonly string OsFilename = "Meadow.OS.bin";
public static readonly string RuntimeFilename = "Meadow.OS.Runtime.bin";
public static readonly string NetworkBootloaderFilename = "bootloader.bin";
public static readonly string NetworkMeadowCommsFilename = "MeadowComms.bin";
public static readonly string NetworkPartitionTableFilename = "partition-table.bin";
internal static readonly string VersionCheckUrlRoot =
"https://s3-us-west-2.amazonaws.com/downloads.wildernesslabs.co/Meadow_Beta/";
internal static readonly string VersionCheckUrlRoot = "https://s3-us-west-2.amazonaws.com/downloads.wildernesslabs.co/Meadow_Beta/";

public static readonly string UpdateCommand = "dotnet tool update WildernessLabs.Meadow.CLI --global";

private static readonly HttpClient Client = new()
{
Timeout = TimeSpan.FromMinutes(5)
};

private readonly ILogger _logger;
public static readonly string FirmwareDownloadsFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
RootFolder, FirmwareFolder);

public DownloadManager(ILoggerFactory loggerFactory)
public static string FirmwareLatestVersion
{
_logger = loggerFactory.CreateLogger<DownloadManager>();
get
{
string latestPath = Path.Combine(FirmwareDownloadsFolder, LatestFilename);
if (File.Exists(latestPath))
{
return File.ReadAllText(latestPath);
}
throw new FileNotFoundException("Latest firmware not found");
}
}

private static readonly HttpClient Client = new() { Timeout = TimeSpan.FromMinutes(1) };

private readonly ILogger _logger;

public DownloadManager(ILogger logger)
{
_logger = logger;
}

internal async Task<string?> DownloadMeadowOSVersionFile(string? version)
{
string versionCheckUrl;
if (version is null || string.IsNullOrWhiteSpace(version))
string versionCheckUrl, versionCheckFile;

if (string.IsNullOrWhiteSpace(version))
{
_logger.LogInformation("Downloading latest version file" + Environment.NewLine);
versionCheckUrl = VersionCheckUrlRoot + "latest.json";
Expand All @@ -77,8 +60,6 @@ public DownloadManager(ILogger logger)
versionCheckUrl = VersionCheckUrlRoot + version + ".json";
}

string versionCheckFile;

try
{
versionCheckFile = await DownloadFile(new Uri(versionCheckUrl));
Expand All @@ -91,65 +72,57 @@ public DownloadManager(ILogger logger)
return versionCheckFile;
}

//ToDo rename this method - DownloadOSAsync?
public async Task DownloadOsBinaries(string? version = null, bool force = false)
{
var versionCheckFilePath = await DownloadMeadowOSVersionFile(version);
var versionFilePath = await DownloadMeadowOSVersionFile(version);

if (versionCheckFilePath == null)
if (versionFilePath == null)
{
_logger.LogError($"Meadow OS {version} cannot be downloaded or is not available");
return;
}

var payload = File.ReadAllText(versionCheckFilePath);
var payload = File.ReadAllText(versionFilePath);
var release = JsonSerializer.Deserialize<ReleaseMetadata>(payload);

if (release == null)
{
_logger.LogError($"Unable to read release details for Meadow OS {version}. Payload: {payload}");
_logger.LogError($"Unable to read release details for Meadow OS");
return;
}

if (!Directory.Exists(FirmwareDownloadsFilePathRoot))
if (Directory.Exists(FirmwareDownloadsFolder) == false)
{
Directory.CreateDirectory(FirmwareDownloadsFilePathRoot);
Directory.CreateDirectory(FirmwareDownloadsFolder);
//we'll write latest.txt regardless of version if it doesn't exist
File.WriteAllText(Path.Combine(FirmwareDownloadsFilePathRoot, "latest.txt"), release.Version);
File.WriteAllText(Path.Combine(FirmwareDownloadsFolder, LatestFilename), release.Version);
}
else if (version == null)
{ //otherwise only update if we're pulling the latest release OS
File.WriteAllText(Path.Combine(FirmwareDownloadsFilePathRoot, "latest.txt"), release.Version);
{ //otherwise update if we're pulling the latest release OS
File.WriteAllText(Path.Combine(FirmwareDownloadsFolder, LatestFilename), release.Version);
}

if (release.Version.ToVersion() < "0.6.0.0".ToVersion())
{
_logger.LogInformation(
$"Downloading OS version {release.Version} is no longer supported. The minimum OS version is 0.6.0.0." + Environment.NewLine);
return;
}
var firmwareVersionPath = Path.Combine(FirmwareDownloadsFolder, release.Version);

var local_path = Path.Combine(FirmwareDownloadsFilePathRoot, release.Version);

if (Directory.Exists(local_path))
if (Directory.Exists(firmwareVersionPath))
{
if (force)
{
CleanPath(local_path);
DeleteDirectory(firmwareVersionPath);
}
else
{
_logger.LogInformation($"Meadow OS version {release.Version} is already downloaded." + Environment.NewLine);
_logger.LogInformation($"Meadow OS version {release.Version} is already downloaded");
return;
}
}

Directory.CreateDirectory(local_path);
Directory.CreateDirectory(firmwareVersionPath);

try
{
_logger.LogInformation($"Downloading Meadow OS" + Environment.NewLine);
await DownloadAndExtractFile(new Uri(release.DownloadURL), local_path);
_logger.LogInformation($"Downloading Meadow OS");
await DownloadAndUnpack(new Uri(release.DownloadURL), firmwareVersionPath);
}
catch
{
Expand All @@ -159,44 +132,16 @@ public async Task DownloadOsBinaries(string? version = null, bool force = false)

try
{
_logger.LogInformation("Downloading coprocessor firmware" + Environment.NewLine);
await DownloadAndExtractFile(new Uri(release.NetworkDownloadURL), local_path);
_logger.LogInformation("Downloading coprocessor firmware");
await DownloadAndUnpack(new Uri(release.NetworkDownloadURL), firmwareVersionPath);
}
catch
{
_logger.LogError($"Unable to download coprocessor firmware {version}");
return;
}

_logger.LogInformation($"Downloaded and extracted OS version {release.Version} to: {local_path}" + Environment.NewLine);
}

public async Task<(bool updateExists, string latestVersion, string currentVersion)> CheckForUpdates()
{
try
{
var packageId = "WildernessLabs.Meadow.CLI";
var appVersion = Assembly.GetEntryAssembly()!
.GetCustomAttribute<AssemblyFileVersionAttribute>()
.Version;

var json = await Client.GetStringAsync(
$"https://api.nuget.org/v3-flatcontainer/{packageId.ToLower()}/index.json");

var result = JsonSerializer.Deserialize<PackageVersions>(json);

if (!string.IsNullOrEmpty(result?.Versions.LastOrDefault()))
{
var latest = result!.Versions!.Last();
return (latest.ToVersion() > appVersion.ToVersion(), latest, appVersion);
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Error checking for updates to Meadow.CLI");
}

return (false, string.Empty, string.Empty);
_logger.LogInformation($"Downloaded and extracted OS version {release.Version} to: {firmwareVersionPath}");
}

private async Task<string> DownloadFile(Uri uri, CancellationToken cancellationToken = default)
Expand All @@ -207,61 +152,49 @@ private async Task<string> DownloadFile(Uri uri, CancellationToken cancellationT
response.EnsureSuccessStatusCode();

var downloadFileName = Path.GetTempFileName();
_logger.LogDebug("Copying downloaded file to temp file {filename}", downloadFileName);
using (var stream = await response.Content.ReadAsStreamAsync())
using (var downloadFileStream = new DownloadFileStream(stream, _logger))
using (var firmwareFile = File.OpenWrite(downloadFileName))
{
await downloadFileStream.CopyToAsync(firmwareFile);
}
_logger.LogDebug($"Copying downloaded file to temp file {downloadFileName}");

using var stream = await response.Content.ReadAsStreamAsync();
using var downloadFileStream = new DownloadFileStream(stream, _logger);
using var firmwareFile = File.OpenWrite(downloadFileName);

await downloadFileStream.CopyToAsync(firmwareFile);

return downloadFileName;
}

private async Task DownloadAndExtractFile(Uri uri, string target_path, CancellationToken cancellationToken = default)
private async Task DownloadAndUnpack(Uri uri, string targetPath, CancellationToken cancellationToken = default)
{
var downloadFileName = await DownloadFile(uri, cancellationToken);
var file = await DownloadFile(uri, cancellationToken);

_logger.LogDebug($"Extracting {file} to {targetPath}");

ZipFile.ExtractToDirectory(file, targetPath);

_logger.LogDebug("Extracting firmware to {path}", target_path);
ZipFile.ExtractToDirectory(
downloadFileName,
target_path);
try
{
File.Delete(downloadFileName);
File.Delete(file);
}
catch (Exception ex)
{
_logger.LogWarning("Unable to delete temporary file");
_logger.LogDebug(ex, "Unable to delete temporary file");
}
}

private void CleanPath(string path)
/// <summary>
/// Delete all files and sub directorines in a directory
/// </summary>
/// <param name="path">The directory path</param>
/// <param name="logger">Optional ILogger for exception reporting</param>
public static void DeleteDirectory(string path, ILogger? logger = null)
{
var di = new DirectoryInfo(path);
foreach (FileInfo file in di.GetFiles())
try
{
try
{
file.Delete();
}
catch (Exception ex)
{
_logger.LogWarning("Failed to delete file {file} in firmware path", file.FullName);
_logger.LogDebug(ex, "Failed to delete file");
}
Directory.Delete(path, true);
}
foreach (DirectoryInfo dir in di.GetDirectories())
catch (IOException e)
{
try
{
dir.Delete(true);
}
catch (Exception ex)
{
_logger.LogWarning("Failed to delete directory {directory} in firmware path", dir.FullName);
_logger.LogDebug(ex, "Failed to delete directory");
}
logger?.LogWarning($"Failed to delete {path} - {e.Message}");
}
}
}
4 changes: 2 additions & 2 deletions Source/v2/Meadow.Hcom/Firmware/FirmwareManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static async Task<string> GetCloudLatestFirmwareVersion()

public static string GetLocalLatestFirmwareVersion()
{
var di = new DirectoryInfo(DownloadManager.FirmwareDownloadsFilePathRoot);
var di = new DirectoryInfo(DownloadManager.FirmwareDownloadsFolder);
var latest = string.Empty;
var latestFile = di.GetFiles("latest.txt").FirstOrDefault();
if (latestFile != null)
Expand All @@ -60,7 +60,7 @@ public static FirmwareInfo[] GetAllLocalFirmwareBuilds()
{
var list = new List<FirmwareInfo>();

var di = new DirectoryInfo(DownloadManager.FirmwareDownloadsFilePathRoot);
var di = new DirectoryInfo(DownloadManager.FirmwareDownloadsFolder);

var latest = GetLocalLatestFirmwareVersion();

Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Hcom/Firmware/PackageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public string CreatePackage(string applicationPath, string osVersion)
throw new ArgumentException($"Invalid applicationPath: {applicationPath}");
}

var osFilePath = Path.Combine(DownloadManager.FirmwareDownloadsFilePathRoot, osVersion);
var osFilePath = Path.Combine(DownloadManager.FirmwareDownloadsFolder, osVersion);
if (!Directory.Exists(osFilePath))
{
throw new ArgumentException($"osVersion {osVersion} not found. Please download.");
Expand Down

0 comments on commit db2fdd7

Please sign in to comment.