mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
WIP NRT for deployment project
This commit is contained in:
@@ -9,15 +9,15 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class AzureDownloadOptions : RepositoryOptions, IObjectDownloadOptions
|
||||
{
|
||||
public string Account { get; set; }
|
||||
public string? Account { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
public string? Key { get; set; }
|
||||
|
||||
public string Endpoint { get; set; }
|
||||
public string? Endpoint { get; set; }
|
||||
|
||||
public string Container { get; set; }
|
||||
public string? Container { get; set; }
|
||||
|
||||
public string SasToken { get; set; }
|
||||
public string? SasToken { get; set; }
|
||||
}
|
||||
|
||||
public class AzureUploadOptions : AzureDownloadOptions, IObjectUploadOptions
|
||||
@@ -62,7 +62,7 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
|
||||
"Deleting " + key);
|
||||
}
|
||||
|
||||
protected override async Task<byte[]> GetObjectBytes(BlobContainerClient client, string key)
|
||||
protected override async Task<byte[]?> GetObjectBytes(BlobContainerClient client, string key)
|
||||
{
|
||||
return await RetryAsyncRet(
|
||||
async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
@@ -12,7 +12,7 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class GitHubHttpClient : IHttpClient
|
||||
{
|
||||
private HttpClient _client;
|
||||
private HttpClient? _client;
|
||||
|
||||
public const string RedirectCountKey = "RedirectCount";
|
||||
|
||||
@@ -34,11 +34,11 @@ public class GitHubHttpClient : IHttpClient
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
_client?.Dispose();
|
||||
_client = null;
|
||||
}
|
||||
|
||||
public async Task<IResponse> Send(IRequest request, CancellationToken cancellationToken, Func<object, object> preprocessResponseBody = null)
|
||||
public async Task<IResponse> Send(IRequest request, CancellationToken cancellationToken, Func<object, object>? preprocessResponseBody = null)
|
||||
{
|
||||
if (_client == null) {
|
||||
throw new ObjectDisposedException(nameof(GitHubHttpClient));
|
||||
@@ -49,25 +49,27 @@ public class GitHubHttpClient : IHttpClient
|
||||
}
|
||||
|
||||
|
||||
using (var requestMessage = BuildRequestMessage(request)) {
|
||||
var responseMessage = await SendAsync(requestMessage).ConfigureAwait(false);
|
||||
using var requestMessage = BuildRequestMessage(request);
|
||||
var responseMessage = await SendAsync(requestMessage).ConfigureAwait(false);
|
||||
|
||||
return await BuildResponse(responseMessage, preprocessResponseBody).ConfigureAwait(false);
|
||||
}
|
||||
return await BuildResponse(responseMessage, preprocessResponseBody).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
// Clone the request/content in case we get a redirect
|
||||
var clonedRequest = await CloneHttpRequestMessageAsync(request).ConfigureAwait(false);
|
||||
|
||||
// Send initial response
|
||||
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None).ConfigureAwait(false);
|
||||
var response = await _client!.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None).ConfigureAwait(false);
|
||||
// Need to determine time on client computer as soon as possible.
|
||||
var receivedTime = DateTimeOffset.Now;
|
||||
|
||||
IDictionary<string, object?> properties = request.Options;
|
||||
|
||||
// Since Properties are stored as objects, serialize to HTTP round-tripping string (Format: r)
|
||||
// Resolution is limited to one-second, matching the resolution of the HTTP Date header
|
||||
request.Properties[ReceivedTimeHeaderName] =
|
||||
properties[ReceivedTimeHeaderName] =
|
||||
receivedTime.ToString("r", CultureInfo.InvariantCulture);
|
||||
|
||||
// Can't redirect without somewhere to redirect to.
|
||||
@@ -77,8 +79,8 @@ public class GitHubHttpClient : IHttpClient
|
||||
|
||||
// Don't redirect if we exceed max number of redirects
|
||||
var redirectCount = 0;
|
||||
if (request.Properties.Keys.Contains(RedirectCountKey)) {
|
||||
redirectCount = (int) request.Properties[RedirectCountKey];
|
||||
if (properties.TryGetValue(RedirectCountKey, out var redirectCountValue) && redirectCountValue is int value) {
|
||||
redirectCount = value;
|
||||
}
|
||||
|
||||
if (redirectCount > 3) {
|
||||
@@ -97,13 +99,13 @@ public class GitHubHttpClient : IHttpClient
|
||||
}
|
||||
|
||||
// Increment the redirect count
|
||||
clonedRequest.Properties[RedirectCountKey] = ++redirectCount;
|
||||
((IDictionary<string, object?>)clonedRequest.Options)[RedirectCountKey] = ++redirectCount;
|
||||
|
||||
// Set the new Uri based on location header
|
||||
clonedRequest.RequestUri = response.Headers.Location;
|
||||
|
||||
// Clear authentication if redirected to a different host
|
||||
if (string.Compare(clonedRequest.RequestUri.Host, request.RequestUri.Host, StringComparison.OrdinalIgnoreCase) != 0) {
|
||||
if (string.Compare(clonedRequest.RequestUri.Host, request.RequestUri!.Host, StringComparison.OrdinalIgnoreCase) != 0) {
|
||||
clonedRequest.Headers.Authorization = null;
|
||||
}
|
||||
|
||||
@@ -116,11 +118,9 @@ public class GitHubHttpClient : IHttpClient
|
||||
|
||||
protected virtual HttpRequestMessage BuildRequestMessage(IRequest request)
|
||||
{
|
||||
if (request == null) {
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
HttpRequestMessage requestMessage = null;
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
HttpRequestMessage? requestMessage = null;
|
||||
try {
|
||||
var fullUri = new Uri(request.BaseAddress, request.Endpoint);
|
||||
requestMessage = new HttpRequestMessage(request.Method, fullUri);
|
||||
@@ -145,9 +145,7 @@ public class GitHubHttpClient : IHttpClient
|
||||
requestMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(request.ContentType);
|
||||
}
|
||||
} catch (Exception) {
|
||||
if (requestMessage != null) {
|
||||
requestMessage.Dispose();
|
||||
}
|
||||
requestMessage?.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -155,7 +153,7 @@ public class GitHubHttpClient : IHttpClient
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
static string GetContentMediaType(HttpContent httpContent)
|
||||
private static string? GetContentMediaType(HttpContent httpContent)
|
||||
{
|
||||
if (httpContent.Headers?.ContentType != null) {
|
||||
return httpContent.Headers.ContentType.MediaType;
|
||||
@@ -170,14 +168,12 @@ public class GitHubHttpClient : IHttpClient
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual async Task<IResponse> BuildResponse(HttpResponseMessage responseMessage, Func<object, object> preprocessResponseBody)
|
||||
protected virtual async Task<IResponse> BuildResponse(HttpResponseMessage responseMessage, Func<object, object>? preprocessResponseBody)
|
||||
{
|
||||
if (responseMessage == null) {
|
||||
throw new ArgumentNullException(nameof(responseMessage));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(responseMessage);
|
||||
|
||||
object responseBody = null;
|
||||
string contentType = null;
|
||||
object? responseBody = null;
|
||||
string? contentType = null;
|
||||
|
||||
// We added support for downloading images,zip-files and application/octet-stream.
|
||||
// Let's constrain this appropriately.
|
||||
@@ -209,7 +205,7 @@ public class GitHubHttpClient : IHttpClient
|
||||
// Add Client response received time as a synthetic header
|
||||
const string receivedTimeHeaderName = ReceivedTimeHeaderName;
|
||||
if (responseMessage.RequestMessage?.Properties is IDictionary<string, object> reqProperties
|
||||
&& reqProperties.TryGetValue(receivedTimeHeaderName, out object receivedTimeObj)
|
||||
&& reqProperties.TryGetValue(receivedTimeHeaderName, out object? receivedTimeObj)
|
||||
&& receivedTimeObj is string receivedTimeString
|
||||
&& !responseHeaders.ContainsKey(receivedTimeHeaderName)) {
|
||||
responseHeaders[receivedTimeHeaderName] = receivedTimeString;
|
||||
@@ -261,12 +257,10 @@ public class GitHubHttpClient : IHttpClient
|
||||
|
||||
private class GitHubResponse : IResponse
|
||||
{
|
||||
public GitHubResponse(HttpStatusCode statusCode, object body, IDictionary<string, string> headers, string contentType)
|
||||
public GitHubResponse(HttpStatusCode statusCode, object? body, IDictionary<string, string> headers, string? contentType)
|
||||
{
|
||||
if (headers == null) {
|
||||
throw new ArgumentNullException(nameof(headers));
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(headers);
|
||||
|
||||
StatusCode = statusCode;
|
||||
Body = body;
|
||||
Headers = new ReadOnlyDictionary<string, string>(headers);
|
||||
@@ -275,7 +269,7 @@ public class GitHubHttpClient : IHttpClient
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Body { get; private set; }
|
||||
public object? Body { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Information about the API.
|
||||
@@ -295,7 +289,7 @@ public class GitHubHttpClient : IHttpClient
|
||||
/// <summary>
|
||||
/// The content type of the response.
|
||||
/// </summary>
|
||||
public string ContentType { get; private set; }
|
||||
public string? ContentType { get; private set; }
|
||||
}
|
||||
|
||||
private static class ApiInfoParser
|
||||
@@ -321,14 +315,12 @@ public class GitHubHttpClient : IHttpClient
|
||||
|
||||
public static ApiInfo ParseResponseHeaders(IDictionary<string, string> responseHeaders)
|
||||
{
|
||||
if (responseHeaders == null) {
|
||||
throw new ArgumentNullException(nameof(responseHeaders));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(responseHeaders);
|
||||
|
||||
var httpLinks = new Dictionary<string, Uri>();
|
||||
var oauthScopes = new List<string>();
|
||||
var acceptedOauthScopes = new List<string>();
|
||||
string etag = null;
|
||||
string? etag = null;
|
||||
|
||||
var acceptedOauthScopesKey = LookupHeader(responseHeaders, "X-Accepted-OAuth-Scopes");
|
||||
if (Exists(acceptedOauthScopesKey)) {
|
||||
|
||||
@@ -4,7 +4,6 @@ using Octokit;
|
||||
using Velopack.Core;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -14,20 +13,20 @@ public class GitHubDownloadOptions : RepositoryOptions
|
||||
{
|
||||
public bool Prerelease { get; set; }
|
||||
|
||||
public string RepoUrl { get; set; }
|
||||
public string? RepoUrl { get; set; }
|
||||
|
||||
public string Token { get; set; }
|
||||
public string? Token { get; set; }
|
||||
}
|
||||
|
||||
public class GitHubUploadOptions : GitHubDownloadOptions
|
||||
{
|
||||
public bool Publish { get; set; }
|
||||
|
||||
public string ReleaseName { get; set; }
|
||||
public string? ReleaseName { get; set; }
|
||||
|
||||
public string TagName { get; set; }
|
||||
public string? TagName { get; set; }
|
||||
|
||||
public string TargetCommitish { get; set; }
|
||||
public string? TargetCommitish { get; set; }
|
||||
|
||||
public bool Merge { get; set; }
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ public class GiteaDownloadOptions : RepositoryOptions
|
||||
{
|
||||
public bool Prerelease { get; set; }
|
||||
|
||||
public string RepoUrl { get; set; }
|
||||
public string? RepoUrl { get; set; }
|
||||
|
||||
public string Token { get; set; }
|
||||
public string? Token { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// Example https://gitea.com
|
||||
@@ -32,11 +32,11 @@ public class GiteaUploadOptions : GiteaDownloadOptions
|
||||
{
|
||||
public bool Publish { get; set; }
|
||||
|
||||
public string ReleaseName { get; set; }
|
||||
public string? ReleaseName { get; set; }
|
||||
|
||||
public string TagName { get; set; }
|
||||
public string? TagName { get; set; }
|
||||
|
||||
public string TargetCommitish { get; set; }
|
||||
public string? TargetCommitish { get; set; }
|
||||
|
||||
public bool Merge { get; set; }
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
|
||||
var apiInstance = new RepositoryApi(config);
|
||||
// Get all releases
|
||||
// Get repository info for total releases
|
||||
List<Release> existingReleases = null;
|
||||
List<Release>? existingReleases = null;
|
||||
ApiResponse<Repository> repositoryInfo = await apiInstance.RepoGetWithHttpInfoAsync(repoOwner, repoName);
|
||||
if (repositoryInfo != null && repositoryInfo.StatusCode == HttpStatusCode.OK) {
|
||||
// Get all releases
|
||||
@@ -182,7 +182,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadFileAsAsset(RepositoryApi client, Release release, string repoOwner, string repoName, string filePath)
|
||||
private static async Task UploadFileAsAsset(RepositoryApi client, Release release, string repoOwner, string repoName, string filePath)
|
||||
{
|
||||
using var stream = File.OpenRead(filePath);
|
||||
// Create a release attachment
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class HttpDownloadOptions : RepositoryOptions
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
|
||||
public class HttpRepository : SourceRepository<HttpDownloadOptions, SimpleWebSource>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class LocalDownloadOptions : RepositoryOptions, IObjectDownloadOptions
|
||||
{
|
||||
public DirectoryInfo TargetPath { get; set; }
|
||||
public DirectoryInfo? TargetPath { get; set; }
|
||||
}
|
||||
|
||||
public class LocalUploadOptions : LocalDownloadOptions, IObjectUploadOptions
|
||||
@@ -32,11 +32,11 @@ public class LocalRepository(ILogger logger) : ObjectRepository<LocalDownloadOpt
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task<byte[]> GetObjectBytes(DirectoryInfo client, string key)
|
||||
protected override async Task<byte[]?> GetObjectBytes(DirectoryInfo client, string key)
|
||||
{
|
||||
var target = Path.Combine(client.FullName, key);
|
||||
Log.Info("Reading: " + target);
|
||||
return File.ReadAllBytesAsync(target);
|
||||
return await File.ReadAllBytesAsync(target);
|
||||
}
|
||||
|
||||
protected override Task UploadObject(DirectoryInfo client, string key, FileInfo f, bool overwriteRemote, bool noCache)
|
||||
|
||||
@@ -8,19 +8,19 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class S3DownloadOptions : RepositoryOptions, IObjectDownloadOptions
|
||||
{
|
||||
public string KeyId { get; set; }
|
||||
public string? KeyId { get; set; }
|
||||
|
||||
public string Secret { get; set; }
|
||||
public string? Secret { get; set; }
|
||||
|
||||
public string Session { get; set; }
|
||||
public string? Session { get; set; }
|
||||
|
||||
public string Region { get; set; }
|
||||
public string? Region { get; set; }
|
||||
|
||||
public string Endpoint { get; set; }
|
||||
public string? Endpoint { get; set; }
|
||||
|
||||
public string Bucket { get; set; }
|
||||
public string? Bucket { get; set; }
|
||||
|
||||
public string Prefix { get; set; }
|
||||
public string? Prefix { get; set; }
|
||||
}
|
||||
|
||||
public class S3UploadOptions : S3DownloadOptions, IObjectUploadOptions
|
||||
@@ -129,7 +129,7 @@ public class S3Repository : ObjectRepository<S3DownloadOptions, S3UploadOptions,
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith("/")) {
|
||||
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith('/')) {
|
||||
prefix += "/";
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class S3Repository : ObjectRepository<S3DownloadOptions, S3UploadOptions,
|
||||
await RetryAsync(() => client.DeleteObjectAsync(key), "Deleting " + key);
|
||||
}
|
||||
|
||||
protected override async Task<byte[]> GetObjectBytes(S3BucketClient client, string key)
|
||||
protected override async Task<byte[]?> GetObjectBytes(S3BucketClient client, string key)
|
||||
{
|
||||
return await RetryAsyncRet(
|
||||
async () => {
|
||||
@@ -165,16 +165,15 @@ public class S3Repository : ObjectRepository<S3DownloadOptions, S3UploadOptions,
|
||||
var client = CreateClient(options);
|
||||
await RetryAsync(
|
||||
async () => {
|
||||
using (var obj = await client.GetObjectAsync(entry.FileName)) {
|
||||
await obj.WriteResponseStreamToFileAsync(filePath, false, CancellationToken.None);
|
||||
}
|
||||
using var obj = await client.GetObjectAsync(entry.FileName);
|
||||
await obj.WriteResponseStreamToFileAsync(filePath, false, CancellationToken.None);
|
||||
},
|
||||
$"Downloading {entry.FileName}...");
|
||||
}
|
||||
|
||||
protected override async Task UploadObject(S3BucketClient client, string key, FileInfo f, bool overwriteRemote, bool noCache)
|
||||
{
|
||||
string deleteOldVersionId = null;
|
||||
string? deleteOldVersionId = null;
|
||||
|
||||
// try to detect an existing remote file of the same name
|
||||
try {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>$(NoWarn);CA2007;CS8002</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging;
|
||||
@@ -25,7 +25,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
|
||||
|
||||
protected abstract Task UploadObject(TClient client, string key, FileInfo f, bool overwriteRemote, bool noCache);
|
||||
protected abstract Task DeleteObject(TClient client, string key);
|
||||
protected abstract Task<byte[]> GetObjectBytes(TClient client, string key);
|
||||
protected abstract Task<byte[]?> GetObjectBytes(TClient client, string key);
|
||||
protected abstract TClient CreateClient(TDown options);
|
||||
|
||||
protected byte[] GetFileMD5Checksum(string filePath)
|
||||
@@ -63,7 +63,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
|
||||
|
||||
Log.Info($"{releaseEntries.Length} merged local/remote release(s).");
|
||||
|
||||
var toDelete = new VelopackAsset[0];
|
||||
var toDelete = Array.Empty<VelopackAsset>();
|
||||
|
||||
if (options.KeepMaxReleases > 0) {
|
||||
var fullReleases = releaseEntries
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Velopack.Deployment;
|
||||
|
||||
public class RepositoryOptions : IOutputOptions
|
||||
{
|
||||
private string _channel;
|
||||
private string? _channel;
|
||||
|
||||
public RuntimeOs TargetOs { get; set; }
|
||||
|
||||
@@ -17,7 +17,7 @@ public class RepositoryOptions : IOutputOptions
|
||||
set => _channel = value;
|
||||
}
|
||||
|
||||
public DirectoryInfo ReleaseDir { get; set; }
|
||||
public DirectoryInfo? ReleaseDir { get; set; }
|
||||
|
||||
public double Timeout { get; set; } = 30d;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user