Skip to content

Automated GitHub Release creation on NuGet publish#45

Open
jfversluis wants to merge 3 commits intomainfrom
feature/release-automation
Open

Automated GitHub Release creation on NuGet publish#45
jfversluis wants to merge 3 commits intomainfrom
feature/release-automation

Conversation

@jfversluis
Copy link
Copy Markdown
Member

Summary

Automates GitHub Release creation so it's a zero-effort byproduct of the existing AzDO publish flow.

How it works

  1. AzDO New create_git_tag stage runs after publish_devflow_nuget and/or publish_cli_nuget succeedpipeline

    • Reads eng/Versions.props to compute the tag (e.g. v0.1.0-preview.4)
    • Skips gracefully if the tag already exists (safe to re-run)
    • Only included when at least one publish parameter is true
  2. GitHub New create-release.yml triggers on v* tag pushActions

    • Auto-generates changelog from PRs since the previous tag
    • Includes NuGet package table with badges
    • Marks preview/rc versions as pre-release

Single trigger point

GitHub Release is created. No manual steps.

Replaces

The manual process of tagging commits and writing release notes (as we just did for preview.3).1

When publishDevFlowNuget or publishCliNuget is set in the AzDO pipeline,
a new create_git_tag stage runs after successful publish. It reads
eng/Versions.props to compute the version tag (e.g. v0.1.0-preview.3)
and pushes it to GitHub.

A new GitHub Actions workflow (create-release.yml) triggers on v* tag
pushes and auto-creates a GitHub Release with:
- NuGet package table with badges
- Auto-generated changelog from PRs since the previous tag
- Prerelease flag for preview/rc versions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 31, 2026 07:42
The Azure Pipelines GitHub App only provides read access to code.
Push the tag using a GitHubPushToken secret variable with repo scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR automates GitHub Release creation as an output of the existing AzDO NuGet publish pipeline by pushing a v* tag after a successful publish and letting a GitHub Actions workflow generate the Release.

Changes:

  • Add an AzDO stage that computes the repo version from eng/Versions.props and pushes an annotated v<version> Git tag after successful NuGet publishing.
  • Add a GitHub Actions workflow that triggers on v* tag pushes and creates a GitHub Release with generated notes and a NuGet package table.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
eng/pipelines/devflow-official.yml Adds a conditional create_git_tag stage to compute/push a version tag after NuGet publish.
.github/workflows/create-release.yml New workflow to create a GitHub Release on v* tags and populate release notes/package badges.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

for pkg in Agent Agent.Core Agent.Gtk Blazor Blazor.Gtk Driver Logging; do
echo "| Microsoft.Maui.DevFlow.${pkg} | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.DevFlow.${pkg}.svg)](https://www.nuget.org/packages/Microsoft.Maui.DevFlow.${pkg}) |"
done

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release notes package table is missing Microsoft.Maui.DevFlow.CLI (the DevFlow global tool package), even though it’s part of the DevFlow product and is likely published alongside the other DevFlow packages. Add it to the table (or generate the list from a source-of-truth) so the GitHub Release accurately reflects the shipped artifacts for the tag.

Suggested change
echo "| Microsoft.Maui.DevFlow.CLI | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.DevFlow.CLI.svg)](https://www.nuget.org/packages/Microsoft.Maui.DevFlow.CLI) |"

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +52
# Build package table
{
echo "### NuGet Packages"
echo ""
echo "| Package | NuGet |"
echo "|---------|-------|"

for pkg in Agent Agent.Core Agent.Gtk Blazor Blazor.Gtk Driver Logging; do
echo "| Microsoft.Maui.DevFlow.${pkg} | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.DevFlow.${pkg}.svg)](https://www.nuget.org/packages/Microsoft.Maui.DevFlow.${pkg}) |"
done

echo "| Microsoft.Maui.Cli | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.Cli.svg)](https://www.nuget.org/packages/Microsoft.Maui.Cli) |"
echo ""
echo "Install with:"
echo '```'
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}.*\""
echo '```'
echo ""
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow always lists both DevFlow and Microsoft.Maui.Cli packages, but the AzDO pipeline can push a tag when either publish parameter is true. That means the GitHub Release could advertise packages that were not actually published for that run/version. Consider either (a) only creating the tag when both products were published, or (b) making the workflow build the package table dynamically (e.g., by querying NuGet for which packages exist at VERSION).

Suggested change
# Build package table
{
echo "### NuGet Packages"
echo ""
echo "| Package | NuGet |"
echo "|---------|-------|"
for pkg in Agent Agent.Core Agent.Gtk Blazor Blazor.Gtk Driver Logging; do
echo "| Microsoft.Maui.DevFlow.${pkg} | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.DevFlow.${pkg}.svg)](https://www.nuget.org/packages/Microsoft.Maui.DevFlow.${pkg}) |"
done
echo "| Microsoft.Maui.Cli | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Maui.Cli.svg)](https://www.nuget.org/packages/Microsoft.Maui.Cli) |"
echo ""
echo "Install with:"
echo '```'
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}.*\""
echo '```'
echo ""
# Helper to check if a NuGet package exists at a given version
package_exists() {
local id="$1"
local ver="$2"
local lower
lower=$(echo "$id" | tr '[:upper:]' '[:lower:]')
# NuGet v3 flat container: https://api.nuget.org/v3-flatcontainer/{id-lower}/{version}/{id-lower}.nuspec
if curl -sSfI "https://api.nuget.org/v3-flatcontainer/${lower}/${ver}/${lower}.nuspec" >/dev/null 2>&1; then
return 0
fi
return 1
}
# Determine which packages actually exist for this VERSION
DEVFLOW_PACKAGES=()
DEVFLOW_AGENT_PUBLISHED=false
for pkg in Agent Agent.Core Agent.Gtk Blazor Blazor.Gtk Driver Logging; do
pkg_id="Microsoft.Maui.DevFlow.${pkg}"
if package_exists "${pkg_id}" "${VERSION}"; then
DEVFLOW_PACKAGES+=("${pkg_id}")
if [ "${pkg_id}" = "Microsoft.Maui.DevFlow.Agent" ]; then
DEVFLOW_AGENT_PUBLISHED=true
fi
fi
done
CLI_ID="Microsoft.Maui.Cli"
if package_exists "${CLI_ID}" "${VERSION}"; then
CLI_PUBLISHED=true
else
CLI_PUBLISHED=false
fi
# Build package table
{
echo "### NuGet Packages"
echo ""
if [ "${#DEVFLOW_PACKAGES[@]}" -eq 0 ] && [ "${CLI_PUBLISHED}" != "true" ]; then
echo "_No NuGet packages were found for version ${VERSION}._"
else
echo "| Package | NuGet |"
echo "|---------|-------|"
for pkg_id in "${DEVFLOW_PACKAGES[@]}"; do
echo "| ${pkg_id} | [![NuGet](https://img.shields.io/nuget/v/${pkg_id}.svg)](https://www.nuget.org/packages/${pkg_id}) |"
done
if [ "${CLI_PUBLISHED}" = "true" ]; then
echo "| ${CLI_ID} | [![NuGet](https://img.shields.io/nuget/v/${CLI_ID}.svg)](https://www.nuget.org/packages/${CLI_ID}) |"
fi
echo ""
# Only show install snippet if the DevFlow Agent package exists at this version
if [ "${DEVFLOW_AGENT_PUBLISHED}" = "true" ]; then
echo "Install with:"
echo '```'
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}.*\""
echo '```'
echo ""
fi
fi

Copilot uses AI. Check for mistakes.
echo ""
echo "Install with:"
echo '```'
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}.*\""
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The install snippet uses --version "${VERSION}.*", which will expand the tag version into something like 0.1.0-preview.3.*. That string is not the actual package version and may not be accepted by NuGet tooling as a valid floating version. Prefer emitting the exact ${VERSION} (or a known-valid floating pattern) to avoid publishing release notes with a non-working install command.

Suggested change
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}.*\""
echo "dotnet add package Microsoft.Maui.DevFlow.Agent --version \"${VERSION}\""

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +68
ARGS=(
"$TAG"
--title "$TAG"
--notes-file "$RUNNER_TEMP/release-notes.md"
--generate-notes
)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gh release create is invoked with both --notes-file (custom NuGet table) and --generate-notes (auto-generated changelog). To ensure the final release body reliably contains both the package table and the generated notes, consider explicitly generating notes (e.g., via the releases/generate-notes API) and concatenating with the table, or generating notes first and then updating the release body. As written, it’s unclear which input will take precedence across gh versions.

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +242
# Create a Git tag after successful NuGet publish.
# This triggers the create-release.yml GitHub Actions workflow
# which auto-creates a GitHub Release with generated notes.
- ${{ if or(eq(parameters.publishDevFlowNuget, true), eq(parameters.publishCliNuget, true)) }}:
- stage: create_git_tag
displayName: 'Create Git Tag'
dependsOn:
- ${{ if eq(parameters.publishDevFlowNuget, true) }}:
- publish_devflow_nuget
- ${{ if eq(parameters.publishCliNuget, true) }}:
- publish_cli_nuget
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_git_tag runs when either publishDevFlowNuget or publishCliNuget is true. Since the downstream GitHub Release workflow currently advertises a fixed set of packages, this can produce a release that implies both product sets were published even when only one publish stage ran. If partial publishes are expected, consider gating tag creation on both publishes (or passing along which product(s) were published so the release notes can be accurate).

Copilot uses AI. Check for mistakes.
…ally

- Add standalone createGitHubRelease parameter instead of checking
  each publish param (no changes needed when products are added)
- Tag stage depends on Validate + publish_using_darc (always exist)
- GitHub Action discovers packages from <PackageId> in csproj files
  instead of a hardcoded list
- gh release create --generate-notes already links PRs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants