GitHub Releases

Create GitHub releases with auto-generated notes, version tags, and file uploads.

Overview

GitHub releases provide a way to package and distribute software versions. This recipe shows how to automate release creation with version tags, auto-generated release notes, and binary uploads.

Basic Release

The simplest release workflow tags the current commit and creates a release with auto-generated notes:

var version = "1.0.0";

// Create and push the tag
Git.Tag($"v{version}");
Git.Push();
Git.PushTags();

// Create the release with auto-generated notes
GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithGeneratedNotes());

GitHub’s auto-generated notes include:

  • All commits since the last release
  • PR titles for merged pull requests
  • List of contributors

Release with Custom Notes

For more control over release notes, use WithNotes():

var version = "1.2.0";
var releaseNotes = @"
## What's New

- Added user authentication system
- Improved performance by 40%
- Fixed memory leak in data processor

## Breaking Changes

- Removed deprecated `ProcessData()` method
- Changed default timeout from 30s to 60s

## Contributors

Thanks to @alice and @bob for their contributions!
";

Git.Tag($"v{version}");
Git.Push();
Git.PushTags();

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithTitle($"v{version} - Authentication & Performance")
    .WithNotes(releaseNotes));

Release with Binary Uploads

Upload compiled binaries, installers, or other files as release assets:

var version = "1.0.0";
var project = Dotnet.Project("./src/MyApp/MyApp.csproj");

// Build for multiple platforms
var runtimes = new[] { "win-x64", "linux-x64", "osx-x64", "osx-arm64" };

foreach (var runtime in runtimes)
{
    Dotnet.Publish(project, o => o
        .WithRuntime(runtime)
        .Output(Root / "dist" / runtime)
        .WithConfiguration(Configuration.Release)
        .AsSelfContained()
        .AsSingleFile());
}

// Copy artifacts to host for upload
Ando.CopyArtifactsToHost("dist", "./dist");

// Create release with file uploads
Git.Tag($"v{version}");
Git.Push();
Git.PushTags();

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithGeneratedNotes()
    .WithFiles(
        "dist/win-x64/MyApp.exe",
        "dist/linux-x64/MyApp",
        "dist/osx-x64/MyApp",
        "dist/osx-arm64/MyApp"));

Renaming Files in Releases

Use the path#name syntax to give uploaded files descriptive names:

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithGeneratedNotes()
    .WithFiles(
        "dist/win-x64/MyApp.exe#MyApp-windows-x64.exe",
        "dist/linux-x64/MyApp#MyApp-linux-x64",
        "dist/osx-x64/MyApp#MyApp-macos-x64",
        "dist/osx-arm64/MyApp#MyApp-macos-arm64"));

Users will see clean names like MyApp-windows-x64.exe instead of just MyApp.exe.

Pre-releases and Drafts

Pre-release Versions

Mark releases as pre-releases for beta or release candidate versions:

var version = "2.0.0-beta.1";

Git.Tag($"v{version}");
Git.Push();
Git.PushTags();

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithTitle($"v{version} (Beta)")
    .WithGeneratedNotes()
    .AsPrerelease());

Pre-releases:

  • Display a “Pre-release” label on GitHub
  • Are excluded from the “latest release” API
  • Allow users to opt-in to beta testing

Draft Releases

Create draft releases for review before publishing:

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithGeneratedNotes()
    .AsDraft());

Draft releases:

  • Are invisible to the public
  • Can be edited in the GitHub UI
  • Must be manually published

Conditional Releases with Profiles

Use profiles to separate build and release steps:

var release = DefineProfile("release");

var project = Dotnet.Project("./src/MyApp/MyApp.csproj");
var version = project.Version; // Read from .csproj

// Always build and test
Dotnet.Build(project);
Dotnet.Test(Dotnet.Project("./tests/MyApp.Tests/MyApp.Tests.csproj"));

// Only release when profile is active
if (release)
{
    // Publish binaries
    Dotnet.Publish(project, o => o
        .Output(Root / "dist")
        .WithConfiguration(Configuration.Release)
        .AsSelfContained());

    Ando.CopyArtifactsToHost("dist", "./dist");

    // Tag and release
    Git.Tag($"v{version}", o => o.WithSkipIfExists());
    Git.Push();
    Git.PushTags();

    GitHub.CreateRelease(o => o
        .WithTag($"v{version}")
        .WithGeneratedNotes()
        .WithFiles("dist/MyApp"));
}

Usage:

# Build and test only
ando

# Build, test, and create release
ando -p release

Version from Project File

Read the version from your .csproj file for single-source-of-truth versioning:

var project = Dotnet.Project("./src/MyApp/MyApp.csproj");
var version = project.Version; // Reads <Version> from .csproj

Log.Info($"Building version {version}");

// Use version throughout the build
Dotnet.Build(project);

GitHub.CreateRelease(o => o
    .WithTag($"v{version}")
    .WithTitle($"MyApp v{version}")
    .WithGeneratedNotes());

Full Example: .NET Tool Release

Complete example for releasing a .NET CLI tool:

// build.csando
var release = DefineProfile("release");

var project = Dotnet.Project("./src/MyTool/MyTool.csproj");
var tests = Dotnet.Project("./tests/MyTool.Tests/MyTool.Tests.csproj");
var version = project.Version;

// Build and test
Dotnet.SdkInstall();
Dotnet.Build(project);
Dotnet.Test(tests);

if (release)
{
    // Publish for all platforms
    var runtimes = new[] { "win-x64", "linux-x64", "linux-arm64", "osx-x64", "osx-arm64" };

    foreach (var runtime in runtimes)
    {
        Log.Info($"Publishing for {runtime}...");
        Dotnet.Publish(project, o => o
            .WithRuntime(runtime)
            .Output(Root / "dist" / runtime)
            .WithConfiguration(Configuration.Release)
            .AsSelfContained()
            .AsSingleFile());
    }

    // Create NuGet package
    Nuget.Pack(project);

    // Copy to host
    Ando.CopyArtifactsToHost("dist", "./dist");

    // Create GitHub release
    Git.Tag($"v{version}", o => o.WithSkipIfExists());
    Git.Push();
    Git.PushTags();

    GitHub.CreateRelease(o => o
        .WithTag($"v{version}")
        .WithTitle($"v{version}")
        .WithGeneratedNotes()
        .WithFiles(
            $"dist/win-x64/MyTool.exe#mytool-win-x64.exe",
            $"dist/linux-x64/MyTool#mytool-linux-x64",
            $"dist/linux-arm64/MyTool#mytool-linux-arm64",
            $"dist/osx-x64/MyTool#mytool-macos-x64",
            $"dist/osx-arm64/MyTool#mytool-macos-arm64"));

    // Push to NuGet.org
    Nuget.EnsureAuthenticated();
    Nuget.Push(project);
}

Options Reference

OptionDescription
WithTag(string)Tag name for the release (e.g., “v1.0.0”)
WithTitle(string)Release title (defaults to tag name)
WithNotes(string)Markdown release notes
WithGeneratedNotes()Auto-generate notes from commits
AsDraft()Create as draft (invisible until published)
AsPrerelease()Mark as pre-release
WithoutPrefix()Don’t add ‘v’ prefix to tag
WithFiles(params string[])Files to upload as assets

See Also