A lightweight, dependency-free .NET library for downloading GitHub release assets. Simply provide a repository URL and keyword filters to get a file stream, byte array, or download directly to disk.
- Zero external dependencies - Uses only built-in .NET libraries
- Source-generation friendly - Clean design suitable for embedding in other assemblies
- Flexible URL parsing - Accepts various GitHub URL formats
- Keyword filtering - Find assets by single or multiple keywords
- Multiple output formats - Stream, byte array, or direct file download
- CLI test runner - Built via build script for testing
- .NET 10 or later
Add the project reference or include the source files directly in your solution.
using GitStream;
using var client = new GitStreamClient();
// Download asset to file
await client.DownloadAssetToFileAsync(
"https://github.com/owner/repo/releases",
"my-app-win-x64.zip",
@"C:\Downloads\my-app-win-x64.zip");Use the included PowerShell build script:
# Clean build artifacts
.\build.ps1 -Clean
# Build library, NuGet package, and CLI test runner
.\build.ps1 -Build
# Clean and build
.\build.ps1 -Clean -BuildOutput structure:
release/
??? lib/ # GitStream.dll, GitStream.xml, GitStream.pdb
??? nupkg/ # GitStream.1.0.0.nupkg
??? cli/
??? bin/ # GitStream.Cli.exe (test runner)
The build script creates a CLI test runner for testing the library:
# Interactive mode (prompts before download)
.\GitStream.Cli.exe <repo-url> <keyword> [keyword2] ...
# Auto-download mode (no prompts)
.\GitStream.Cli.exe -y <repo-url> <keyword> [keyword2] ...
.\GitStream.Cli.exe --yes <repo-url> <keyword> [keyword2] ...Examples:
# Find and optionally download an asset
.\GitStream.Cli.exe https://github.com/owner/repo my-app-win-x64.zip
# Auto-download matching asset
.\GitStream.Cli.exe -y https://github.com/owner/repo win-x64 .zipThe main entry point for interacting with GitHub releases.
// Creates client with internal HttpClient (recommended)
public GitStreamClient()
// Uses provided HttpClient (for DI scenarios)
public GitStreamClient(HttpClient httpClient)| Method | Description |
|---|---|
GetAssetStreamAsync |
Returns a Stream for the matching asset |
GetAssetBytesAsync |
Returns a byte[] for the matching asset |
DownloadAssetToFileAsync |
Downloads the asset directly to a file |
FindAssetAsync |
Returns asset metadata without downloading |
GetReleasesAsync |
Gets all releases for a repository |
GetLatestReleaseAsync |
Gets the latest release for a repository |
DownloadAssetStreamAsync |
Downloads from a direct URL |
Static utility for parsing GitHub URLs.
// Parse a GitHub URL
GitHubRepoInfo? info = GitHubUrlParser.Parse("https://github.com/owner/repo");public readonly record struct GitHubRepoInfo(string Owner, string Repo)
{
public string FullName { get; } // "owner/repo"
}public readonly record struct GitHubRelease(
string TagName,
string Name,
bool Prerelease,
DateTimeOffset? PublishedAt,
IReadOnlyList<GitHubReleaseAsset> Assets);public readonly record struct GitHubReleaseAsset(
string Name,
string DownloadUrl,
long Size,
string ContentType);using var client = new GitStreamClient();
await using var stream = await client.GetAssetStreamAsync(
"https://github.com/owner/repo/releases",
"my-app-win-x64.zip");
if (stream is not null)
{
// Process the stream
}using var client = new GitStreamClient();
byte[]? bytes = await client.GetAssetBytesAsync(
"https://github.com/owner/repo/releases",
"my-app-win-x64.zip");
if (bytes is not null)
{
// Use the bytes
}using var client = new GitStreamClient();
bool success = await client.DownloadAssetToFileAsync(
"https://github.com/owner/repo/releases",
"my-app-win-x64.zip",
@"C:\Downloads\my-app-win-x64.zip");
if (success)
{
Console.WriteLine("Download complete!");
}When you need to match multiple patterns in the asset filename:
using var client = new GitStreamClient();
// Asset must contain BOTH "win-x64" AND ".zip"
await using var stream = await client.GetAssetStreamAsync(
"https://github.com/owner/repo/releases",
["win-x64", ".zip"]);using var client = new GitStreamClient();
var releases = await client.GetReleasesAsync(
"https://github.com/owner/repo");
foreach (var release in releases)
{
Console.WriteLine($"{release.TagName} - {release.Name}");
foreach (var asset in release.Assets)
{
Console.WriteLine($" {asset.Name} ({asset.Size} bytes)");
}
}using var client = new GitStreamClient();
var latest = await client.GetLatestReleaseAsync(
"https://github.com/owner/repo");
if (latest is { } release)
{
Console.WriteLine($"Latest: {release.TagName}");
Console.WriteLine($"Published: {release.PublishedAt}");
Console.WriteLine($"Assets: {release.Assets.Count}");
}using var client = new GitStreamClient();
var asset = await client.FindAssetAsync(
"https://github.com/owner/repo/releases",
["my-app-win-x64.zip"]);
if (asset is { } found)
{
Console.WriteLine($"Found: {found.Name}");
Console.WriteLine($"Size: {found.Size} bytes");
Console.WriteLine($"URL: {found.DownloadUrl}");
}// In your DI setup
services.AddHttpClient<GitStreamClient>();
// Or with custom configuration
services.AddHttpClient<GitStreamClient>(client =>
{
client.Timeout = TimeSpan.FromMinutes(10);
});The library accepts various GitHub URL formats:
// All of these work:
"https://github.com/owner/repo"
"https://github.com/owner/repo/releases"
"https://github.com/owner/repo.git"
"http://github.com/owner/repo"
"github.com/owner/repo"
"[email protected]:owner/repo.git"using var client = new GitStreamClient();
try
{
await using var stream = await client.GetAssetStreamAsync(
"https://github.com/owner/repo",
"asset.zip");
if (stream is null)
{
Console.WriteLine("Asset not found");
return;
}
// Process stream
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid URL: {ex.Message}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Network error: {ex.Message}");
}GitHub API has rate limits for unauthenticated requests (60 requests/hour). For higher limits, configure authentication on the HttpClient:
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "your-github-token");
using var client = new GitStreamClient(httpClient);MIT