Skip to content

Commit

Permalink
More complete AzureClient
Browse files Browse the repository at this point in the history
Can get release files and download artifacts, apply logic still pending (with testing as well)
  • Loading branch information
Azyyyyyy committed Jan 26, 2024
1 parent 31393cf commit 9a1bbde
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 126 deletions.
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<PropertyGroup>
<Authors>Azyyyyyy</Authors>
<RepositoryUrl>https://github.com/Azyyyyyy/TinyUpdate</RepositoryUrl>
<Version>2024.01.16-alpha</Version>
<AssemblyVersion>2024.01.16</AssemblyVersion>
<FileVersion>2024.01.16</FileVersion>
<Version>2024.01.26-alpha</Version>
<AssemblyVersion>2024.01.26</AssemblyVersion>
<FileVersion>2024.01.26</FileVersion>
<Description>Cross platform updater that's small on space but big in function!</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>

Expand Down
191 changes: 77 additions & 114 deletions src/Clients/TinyUpdate.Azure/AzureClient.cs
Original file line number Diff line number Diff line change
@@ -1,151 +1,114 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Azure.Pipelines.WebApi;
using System.IO.Compression;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using SemVersion;
using Microsoft.TeamFoundation.Build.WebApi;
using TinyUpdate.Core.Abstract;
using Artifact = Microsoft.Azure.Pipelines.WebApi.Artifact;

namespace TinyUpdate.Azure;

public class AzureClient : IPackageClient
{
const string c_collectionUri = "";
const string project = "";
const int pipelineId = 1;
private const string personalAccessToken = "";

private readonly Uri _orgUrl = new Uri(c_collectionUri);

private readonly HttpClient _downloadClient;
private readonly IUpdateApplier _updateApplier;
private readonly ILogger _logger;
private readonly IHasher _hasher;
public AzureClient(IUpdateApplier updateApplier, ILogger logger, HttpClient downloadClient, IHasher hasher)
private readonly ILogger<AzureClient> _logger;
private readonly AzureClientSettings _clientSettings;
public AzureClient(IUpdateApplier updateApplier, ILogger<AzureClient> logger, AzureClientSettings clientSettings)
{
_updateApplier = updateApplier;
_logger = logger;
_downloadClient = downloadClient;
_hasher = hasher;
_clientSettings = clientSettings;
}

//TODO: REDO
public async IAsyncEnumerable<ReleaseEntry> GetUpdates()
{
using var connection = MakeConnection();
using var pipelineConnection = new PipelinesHttpClient(connection.Uri, connection.Credentials);

//TODO: Find the first release which allow
var runs = await pipelineConnection.ListRunsAsync(project, pipelineId);
foreach (var run in runs)
IAsyncEnumerable<ReleaseEntry?>? releaseEntries = null;
using var buildClient = new BuildHttpClient(_clientSettings.OrganisationUri, _clientSettings.Credentials);
var builds = await buildClient.GetBuildsAsync(
_clientSettings.ProjectGuid,
statusFilter: BuildStatus.Completed,
resultFilter: BuildResult.Succeeded,
top: 1);

var build = builds.FirstOrDefault();
if (build == null) yield break;

var releaseArtifact = await GetArtifactAsync(build.Id, "RELEASE", buildClient);
if (releaseArtifact == null) yield break;

using var downloadClient = new DownloadHttpClient(_clientSettings.OrganisationUri, _clientSettings.Credentials);
try
{
if (run == null || run.State != RunState.Completed
|| run.Result.GetValueOrDefault(RunResult.Failed) != RunResult.Succeeded)
{
continue;
}

Artifact artifact;
try
{
artifact = await pipelineConnection.GetArtifactAsync(project, pipelineId, run.Id, "RELEASE");
}
catch (Exception e)
{
_logger.LogError(e, "Failed to get RELEASE artifact");
continue;
}
await using var releaseArtifactZipStream =
await downloadClient.GetStream(releaseArtifact.Resource.DownloadUrl);
using var releaseZip = new ZipArchive(releaseArtifactZipStream, ZipArchiveMode.Read);
await using var releaseStream = releaseZip.GetEntry("RELEASE/RELEASE")?.Open() ?? Stream.Null;

if (artifact.SignedContent.SignatureExpires >= DateTime.Now)
{
_logger.LogWarning("Unable to download RELEASE file");
yield break;
}

await using var releaseFileStream = await _downloadClient.GetStreamAsync(artifact.SignedContent.Url);
IAsyncEnumerable<AzureReleaseEntry?> releases;
releaseEntries = JsonSerializer.DeserializeAsyncEnumerable<ReleaseEntry>(releaseStream);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to download RELEASE file");
}

try
{
releases = JsonSerializer.DeserializeAsyncEnumerable<AzureReleaseEntry>(releaseFileStream);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to download RELEASE artifact");
continue;
}

await foreach (var release in releases)
if (releaseEntries != null)
{
await foreach (var releaseEntry in releaseEntries)
{
if (release != null && _hasher.IsValidHash(release.Hash))
if (releaseEntry != null)
{
yield return release;
yield return releaseEntry;
}
}
}
}

public Task<bool> DownloadUpdate(ReleaseEntry releaseEntry, IProgress<double>? progress)
public async Task<bool> DownloadUpdate(ReleaseEntry releaseEntry, IProgress<double>? progress)
{
throw new NotImplementedException();
if (releaseEntry is not AzureReleaseEntry azureReleaseEntry)
{
return false;
}

using var buildClient = new BuildHttpClient(_clientSettings.OrganisationUri, _clientSettings.Credentials);

try
{
var dir = Path.Combine(Path.GetTempPath(), "TinyUpdate", "Azure", _clientSettings.ProjectGuid.ToString());
Directory.CreateDirectory(dir);

azureReleaseEntry.ArtifactDownloadPath = Path.Combine(dir, azureReleaseEntry.FileName);

await using var artifactStream =
await buildClient.GetArtifactContentZipAsync(_clientSettings.ProjectGuid, azureReleaseEntry.RunId, azureReleaseEntry.ArtifactName);
await using var fileStream = File.OpenWrite(azureReleaseEntry.ArtifactDownloadPath);
await artifactStream.CopyToAsync(fileStream);

//TODO: Add some checks to the downloaded file?
return true;
}
catch (Exception e)
{
_logger.LogError(e, "Failed to download '{RunName}' artifact ({Artifact})", azureReleaseEntry.RunId, azureReleaseEntry.ArtifactName);
return false;
}
}

public Task<bool> ApplyUpdate(ReleaseEntry releaseEntry, IProgress<double>? progress)
{
//TODO: Load the update package in
throw new NotImplementedException();
}

private VssConnection MakeConnection() => new VssConnection(_orgUrl, new VssBasicCredential(string.Empty, personalAccessToken));
}

public class AzureReleaseEntry : ReleaseEntry
{
public AzureReleaseEntry(string hash, long filesize, SemanticVersion? previousVersion, SemanticVersion newVersion, string fileName, bool isDelta, int runId, string artifactName)
private Task<BuildArtifact?> GetArtifactAsync(int runId, string artifactName, BuildHttpClient buildClient)
{
Hash = hash;
Filesize = filesize;
PreviousVersion = previousVersion;
NewVersion = newVersion;
FileName = fileName;
IsDelta = isDelta;
RunId = runId;
ArtifactName = artifactName;
}

[JsonIgnore]
public override bool HasUpdate => true;

public string Hash { get; }

public long Filesize { get; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault),
JsonConverter(typeof(SemanticVersionConverter))]
public SemanticVersion? PreviousVersion { get; }

[JsonConverter(typeof(SemanticVersionConverter))]
public SemanticVersion NewVersion { get; }

public string FileName { get; }

public bool IsDelta { get; }

public int RunId { get; }

public string ArtifactName { get; }
}

public class SemanticVersionConverter : JsonConverter<SemanticVersion>
{
public override SemanticVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return SemanticVersion.Parse(reader.GetString());
}
try
{
return buildClient.GetArtifactAsync(_clientSettings.ProjectGuid, runId, artifactName);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to get pipeline artifact '{ArtifactName}'", artifactName);
}

public override void Write(Utf8JsonWriter writer, SemanticVersion value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
return Task.FromResult<BuildArtifact?>(null);
}
}
12 changes: 12 additions & 0 deletions src/Clients/TinyUpdate.Azure/AzureClientSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.VisualStudio.Services.Common;

namespace TinyUpdate.Azure;

public class AzureClientSettings
{
public Uri OrganisationUri { get; set; }

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 7 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'OrganisationUri' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public VssCredentials Credentials { get; set; }

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureClientSettings.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'Credentials' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public Guid ProjectGuid { get; set; }
}
45 changes: 45 additions & 0 deletions src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Text.Json.Serialization;
using SemVersion;
using TinyUpdate.Core.Abstract;

namespace TinyUpdate.Azure;

public class AzureReleaseEntry : ReleaseEntry
{
public AzureReleaseEntry(string hash, long filesize, SemanticVersion? previousVersion, SemanticVersion newVersion, string fileName, bool isDelta, int runId, string artifactName)

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 9 in src/Clients/TinyUpdate.Azure/AzureReleaseEntry.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Non-nullable property 'ArtifactDownloadPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
{
Hash = hash;
Filesize = filesize;
PreviousVersion = previousVersion;
NewVersion = newVersion;
FileName = fileName;
IsDelta = isDelta;
RunId = runId;
ArtifactName = artifactName;
}

[JsonIgnore]
public override bool HasUpdate => true;

public string Hash { get; }

public long Filesize { get; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault),
JsonConverter(typeof(SemanticVersionConverter))]
public SemanticVersion? PreviousVersion { get; }

[JsonConverter(typeof(SemanticVersionConverter))]
public SemanticVersion NewVersion { get; }

public string FileName { get; }

public bool IsDelta { get; }

public int RunId { get; }

public string ArtifactName { get; }

[JsonIgnore]
public string ArtifactDownloadPath { get; set; }
}
9 changes: 9 additions & 0 deletions src/Clients/TinyUpdate.Azure/DownloadHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;

namespace TinyUpdate.Azure;

public class DownloadHttpClient(Uri baseUrl, VssCredentials credentials) : VssHttpClientBase(baseUrl, credentials)
{
public Task<Stream> GetStream(string downloadUrl) => Client.GetStreamAsync(downloadUrl);
};
18 changes: 18 additions & 0 deletions src/Clients/TinyUpdate.Azure/SemanticVersionConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using SemVersion;

namespace TinyUpdate.Azure;

public class SemanticVersionConverter : JsonConverter<SemanticVersion>
{
public override SemanticVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return SemanticVersion.Parse(reader.GetString());
}

public override void Write(Utf8JsonWriter writer, SemanticVersion value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace TinyUpdate.MicrosoftStore;

public class MicrosoftStoreClient(StoreContext storeContext, ILogger logger) : IPackageClient
public class MicrosoftStoreClient(StoreContext storeContext, ILogger<MicrosoftStoreClient> logger) : IPackageClient
{
public async IAsyncEnumerable<ReleaseEntry> GetUpdates()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ POSSIBILITY OF SUCH DAMAGE.
/// <summary>
/// Provides creating and applying BSDiff delta's
/// </summary>
public partial class BSDiffDelta(ILogger logger) : IDeltaApplier, IDeltaCreation
public partial class BSDiffDelta(ILogger<BSDiffDelta> logger) : IDeltaApplier, IDeltaCreation
{
private const long CFileSignature = 0x3034464649445342L;
private const int CHeaderSize = 32;
Expand Down
2 changes: 1 addition & 1 deletion src/TinyUpdate.Core/DeltaManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace TinyUpdate.Core;
/// <summary>
/// Default <see cref="IDeltaManager"/> implementation
/// </summary>
public class DeltaManager(IEnumerable<IDeltaApplier> appliers, IEnumerable<IDeltaCreation> creators, ILogger logger)
public class DeltaManager(IEnumerable<IDeltaApplier> appliers, IEnumerable<IDeltaCreation> creators, ILogger<DeltaManager> logger)
: IDeltaManager
{
private readonly AsyncLock _copyLock = new AsyncLock();
Expand Down
5 changes: 2 additions & 3 deletions src/Updaters/TinyUpdate.Desktop/DesktopApplier.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
using Microsoft.Extensions.Logging;
using TinyUpdate.Core.Abstract.Delta;
using TinyUpdate.Desktop.Abstract;
using TinyUpdate.Desktop.Native;

namespace TinyUpdate.Desktop;

public class DesktopApplier : IUpdateApplier
{
private readonly IHasher _hasher;
private readonly ILogger _logger;
private readonly ILogger<DesktopApplier> _logger;
private readonly IDeltaManager _deltaManager;
private readonly INative? _native;
public DesktopApplier(ILogger logger, IHasher hasher, INative? native, IDeltaManager deltaManager)
public DesktopApplier(ILogger<DesktopApplier> logger, IHasher hasher, INative? native, IDeltaManager deltaManager)
{
_logger = logger;
_hasher = hasher;
Expand Down
2 changes: 1 addition & 1 deletion tests/TinyUpdate.Delta.Tests/DeltaManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ namespace TinyUpdate.Delta.Tests;

public class DeltaManagerTests : DeltaManagerCan
{
protected override IDeltaManager CreateDeltaManager(IEnumerable<IDeltaApplier> appliers, IEnumerable<IDeltaCreation> creators) => new DeltaManager(appliers, creators, NullLogger.Instance);
protected override IDeltaManager CreateDeltaManager(IEnumerable<IDeltaApplier> appliers, IEnumerable<IDeltaCreation> creators) => new DeltaManager(appliers, creators, NullLogger<DeltaManager>.Instance);
}
2 changes: 1 addition & 1 deletion tests/TinyUpdate.Delta.Tests/DeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class BSDiffDeltaTests : DeltaCan
[OneTimeSetUp]
public void OneTimeSetup()
{
var delta = new BSDiffDelta(NullLogger.Instance);
var delta = new BSDiffDelta(NullLogger<BSDiffDelta>.Instance);
Creator = delta;
Applier = delta;
}
Expand Down
Loading

0 comments on commit 9a1bbde

Please sign in to comment.