mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Fix bug in GithubSource when an access token is not provided
This commit is contained in:
@@ -37,7 +37,7 @@ namespace Velopack.Sources
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Bearer or other type of Authorization header used to authenticate against the Api.
|
/// The Bearer or other type of Authorization header used to authenticate against the Api.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract (string Name, string Value) Authorization { get; }
|
protected abstract (string Name, string Value)? Authorization { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base constructor.
|
/// Base constructor.
|
||||||
@@ -51,17 +51,18 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Task DownloadReleaseEntry(IVelopackLogger logger, VelopackAsset releaseEntry, string localFile, Action<int> progress, CancellationToken cancelToken)
|
public virtual Task DownloadReleaseEntry(IVelopackLogger logger, VelopackAsset releaseEntry, string localFile, Action<int> progress,
|
||||||
|
CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
if (releaseEntry is GitBaseAsset githubEntry) {
|
if (releaseEntry is GitBaseAsset githubEntry) {
|
||||||
// this might be a browser url or an api url (depending on whether we have a AccessToken or not)
|
// this might be a browser url or an api url (depending on whether we have a AccessToken or not)
|
||||||
// https://docs.github.com/en/rest/reference/releases#get-a-release-asset
|
// https://docs.github.com/en/rest/reference/releases#get-a-release-asset
|
||||||
var assetUrl = GetAssetUrlFromName(githubEntry.Release, releaseEntry.FileName);
|
var assetUrl = GetAssetUrlFromName(githubEntry.Release, releaseEntry.FileName);
|
||||||
return Downloader.DownloadFile(assetUrl, localFile, progress,
|
return Downloader.DownloadFile(
|
||||||
new Dictionary<string, string> {
|
assetUrl,
|
||||||
[Authorization.Name] = Authorization.Value,
|
localFile,
|
||||||
["Accept"] = "application/octet-stream"
|
progress,
|
||||||
},
|
GetRequestHeaders("application/octet-stream"),
|
||||||
cancelToken: cancelToken);
|
cancelToken: cancelToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,11 +92,10 @@ namespace Velopack.Sources
|
|||||||
logger.Trace(ex.ToString());
|
logger.Trace(ex.ToString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var releaseBytes = await Downloader.DownloadBytes(assetUrl,
|
|
||||||
new Dictionary<string, string> {
|
var releaseBytes = await Downloader.DownloadBytes(
|
||||||
[Authorization.Name] = Authorization.Value,
|
assetUrl,
|
||||||
["Accept"] = "application/octet-stream"
|
GetRequestHeaders("application/octet-stream")
|
||||||
}
|
|
||||||
).ConfigureAwait(false);
|
).ConfigureAwait(false);
|
||||||
var txt = CoreUtil.RemoveByteOrderMarkerIfPresent(releaseBytes);
|
var txt = CoreUtil.RemoveByteOrderMarkerIfPresent(releaseBytes);
|
||||||
var feed = VelopackAssetFeed.FromJson(txt);
|
var feed = VelopackAssetFeed.FromJson(txt);
|
||||||
@@ -122,6 +122,24 @@ namespace Velopack.Sources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract string GetAssetUrlFromName(T release, string assetName);
|
protected abstract string GetAssetUrlFromName(T release, string assetName);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a dictionary containing HTTP request headers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accept">The value for the "Accept" header; defaults to "application/json".</param>
|
||||||
|
/// <returns>A dictionary of headers including "Accept" and, if available, authorization headers.</returns>
|
||||||
|
protected virtual Dictionary<string, string> GetRequestHeaders(string accept = "application/json")
|
||||||
|
{
|
||||||
|
var headers = new Dictionary<string, string> {
|
||||||
|
["Accept"] = accept,
|
||||||
|
};
|
||||||
|
if (Authorization.HasValue) {
|
||||||
|
headers.Add(Authorization.Value.Name, Authorization.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a wrapper around <see cref="VelopackAsset"/> which also contains a Git Release.
|
/// Provides a wrapper around <see cref="VelopackAsset"/> which also contains a Git Release.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -146,4 +164,4 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,8 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Authorization"/>
|
/// <inheritdoc cref="Authorization"/>
|
||||||
protected override (string Name, string Value) Authorization => ("Authorization", $"token {AccessToken}");
|
protected override (string Name, string Value)? Authorization =>
|
||||||
|
string.IsNullOrEmpty(AccessToken) ? null : ("Authorization", $"token {AccessToken}");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task<GiteaRelease[]> GetReleases(bool includePrereleases)
|
protected override async Task<GiteaRelease[]> GetReleases(bool includePrereleases)
|
||||||
@@ -96,14 +97,9 @@ namespace Velopack.Sources
|
|||||||
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?limit={perPage}&page={page}&draft=false";
|
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?limit={perPage}&page={page}&draft=false";
|
||||||
var baseUri = GetApiBaseUrl(RepoUri);
|
var baseUri = GetApiBaseUrl(RepoUri);
|
||||||
var getReleasesUri = new Uri(baseUri, releasesPath);
|
var getReleasesUri = new Uri(baseUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
var response = await Downloader.DownloadString(getReleasesUri.ToString(), GetRequestHeaders()).ConfigureAwait(false);
|
||||||
new Dictionary<string, string> {
|
|
||||||
[Authorization.Name] = Authorization.Value,
|
|
||||||
["Accept"] = "application/json"
|
|
||||||
}
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
var releases = CompiledJson.DeserializeGiteaReleaseList(response);
|
var releases = CompiledJson.DeserializeGiteaReleaseList(response);
|
||||||
if (releases == null) return new GiteaRelease[0];
|
if (releases == null) return Array.Empty<GiteaRelease>();
|
||||||
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Authorization"/>
|
/// <inheritdoc cref="Authorization"/>
|
||||||
protected override (string Name, string Value) Authorization => ("Authorization", $"Bearer {AccessToken}");
|
protected override (string Name, string Value)? Authorization =>
|
||||||
|
string.IsNullOrEmpty(AccessToken) ? null : ("Authorization", $"Bearer {AccessToken}");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task<GithubRelease[]> GetReleases(bool includePrereleases)
|
protected override async Task<GithubRelease[]> GetReleases(bool includePrereleases)
|
||||||
@@ -99,11 +100,9 @@ namespace Velopack.Sources
|
|||||||
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
var releasesPath = $"repos{RepoUri.AbsolutePath}/releases?per_page={perPage}&page={page}";
|
||||||
var baseUri = GetApiBaseUrl(RepoUri);
|
var baseUri = GetApiBaseUrl(RepoUri);
|
||||||
var getReleasesUri = new Uri(baseUri, releasesPath);
|
var getReleasesUri = new Uri(baseUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
var response = await Downloader.DownloadString(
|
||||||
new Dictionary<string, string> {
|
getReleasesUri.ToString(),
|
||||||
[Authorization.Name] = Authorization.Value,
|
GetRequestHeaders("application/vnd.github.v3+json")
|
||||||
["Accept"] = "application/vnd.github.v3+json"
|
|
||||||
}
|
|
||||||
).ConfigureAwait(false);
|
).ConfigureAwait(false);
|
||||||
var releases = CompiledJson.DeserializeGithubReleaseList(response);
|
var releases = CompiledJson.DeserializeGithubReleaseList(response);
|
||||||
if (releases == null) return Array.Empty<GithubRelease>();
|
if (releases == null) return Array.Empty<GithubRelease>();
|
||||||
@@ -117,7 +116,8 @@ namespace Velopack.Sources
|
|||||||
throw new ArgumentException($"No assets found in GitHub Release '{release.Name}'.");
|
throw new ArgumentException($"No assets found in GitHub Release '{release.Name}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<GithubReleaseAsset> allReleasesFiles = release.Assets.Where(a => a.Name?.Equals(assetName, StringComparison.InvariantCultureIgnoreCase) == true);
|
IEnumerable<GithubReleaseAsset> allReleasesFiles =
|
||||||
|
release.Assets.Where(a => a.Name?.Equals(assetName, StringComparison.InvariantCultureIgnoreCase) == true);
|
||||||
if (!allReleasesFiles.Any()) {
|
if (!allReleasesFiles.Any()) {
|
||||||
throw new ArgumentException($"Could not find asset called '{assetName}' in GitHub Release '{release.Name}'.");
|
throw new ArgumentException($"Could not find asset called '{assetName}' in GitHub Release '{release.Name}'.");
|
||||||
}
|
}
|
||||||
@@ -157,8 +157,9 @@ namespace Velopack.Sources
|
|||||||
// API location is http://internal.github.server.local/api/v3
|
// API location is http://internal.github.server.local/api/v3
|
||||||
baseAddress = new Uri(string.Format("{0}{1}{2}/api/v3/", repoUrl.Scheme, Uri.SchemeDelimiter, repoUrl.Host));
|
baseAddress = new Uri(string.Format("{0}{1}{2}/api/v3/", repoUrl.Scheme, Uri.SchemeDelimiter, repoUrl.Host));
|
||||||
}
|
}
|
||||||
|
|
||||||
// above ^^ notice the end slashes for the baseAddress, explained here: http://stackoverflow.com/a/23438417/162694
|
// above ^^ notice the end slashes for the baseAddress, explained here: http://stackoverflow.com/a/23438417/162694
|
||||||
return baseAddress;
|
return baseAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,8 @@ namespace Velopack.Sources
|
|||||||
public class GitlabSource : GitBase<GitlabRelease>
|
public class GitlabSource : GitBase<GitlabRelease>
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="Authorization"/>
|
/// <inheritdoc cref="Authorization"/>
|
||||||
protected override (string Name, string Value) Authorization => ("PRIVATE-TOKEN", AccessToken ?? string.Empty);
|
protected override (string Name, string Value)? Authorization =>
|
||||||
|
string.IsNullOrEmpty(AccessToken) ? null : ("PRIVATE-TOKEN", AccessToken ?? string.Empty);
|
||||||
|
|
||||||
/// <inheritdoc cref="GitlabSource" />
|
/// <inheritdoc cref="GitlabSource" />
|
||||||
/// <param name="repoUrl">
|
/// <param name="repoUrl">
|
||||||
@@ -159,11 +160,7 @@ namespace Velopack.Sources
|
|||||||
// https://docs.gitlab.com/ee/api/releases/
|
// https://docs.gitlab.com/ee/api/releases/
|
||||||
var releasesPath = $"releases?per_page={perPage}&page={page}";
|
var releasesPath = $"releases?per_page={perPage}&page={page}";
|
||||||
var getReleasesUri = CombineUri(RepoUri, releasesPath);
|
var getReleasesUri = CombineUri(RepoUri, releasesPath);
|
||||||
var response = await Downloader.DownloadString(getReleasesUri.ToString(),
|
var response = await Downloader.DownloadString(getReleasesUri.ToString(), GetRequestHeaders()).ConfigureAwait(false);
|
||||||
new Dictionary<string, string> {
|
|
||||||
[Authorization.Name] = Authorization.Value,
|
|
||||||
["Accept"] = "application/json"
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
var releases = CompiledJson.DeserializeGitlabReleaseList(response);
|
var releases = CompiledJson.DeserializeGitlabReleaseList(response);
|
||||||
if (releases == null) return Array.Empty<GitlabRelease>();
|
if (releases == null) return Array.Empty<GitlabRelease>();
|
||||||
return releases.OrderByDescending(d => d.ReleasedAt).Where(x => includePrereleases || !x.UpcomingRelease).ToArray();
|
return releases.OrderByDescending(d => d.ReleasedAt).Where(x => includePrereleases || !x.UpcomingRelease).ToArray();
|
||||||
|
|||||||
@@ -20,6 +20,26 @@ public class GithubDeploymentTests
|
|||||||
_output = output;
|
_output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "Need to create a repo to test with")]
|
||||||
|
public async Task TestUnauthenticatedDownload()
|
||||||
|
{
|
||||||
|
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
||||||
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
|
|
||||||
|
var repo = new GitHubRepository(logger);
|
||||||
|
var options = new GitHubDownloadOptions {
|
||||||
|
TargetOs = RuntimeOs.Linux,
|
||||||
|
Channel = "linux-x64",
|
||||||
|
ReleaseDir = new DirectoryInfo(releaseDir),
|
||||||
|
Timeout = 60,
|
||||||
|
Prerelease = false,
|
||||||
|
RepoUrl = "TODO",
|
||||||
|
Token = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await repo.DownloadLatestFullPackageAsync(options);
|
||||||
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void WillRefuseToUploadMultipleWithoutMergeArg()
|
public void WillRefuseToUploadMultipleWithoutMergeArg()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user