Skip to content

Bump MAUI

Bump MAUI #28

Workflow file for this run

name: Bump MAUI
# Nightly check for a newer MAUI patch. Unlike before, the bump is now VALIDATED before a PR
# is opened: the change is pushed to a branch, built across all target frameworks, and run
# through the device tests. The PR is only opened when the build gate passes, so a MAUI patch
# that fails to restore/compile against the native bindings never reaches review as a green PR.
on:
schedule:
- cron: '0 6 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: bump-maui
jobs:
check:
runs-on: ubuntu-latest
outputs:
bump: ${{ steps.check.outputs.bump }}
current: ${{ steps.check.outputs.current }}
latest: ${{ steps.check.outputs.latest }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Check NuGet for a newer MAUI release
id: check
run: |
current=$(sed -n 's:.*<MauiPackageVersion>\(.*\)</MauiPackageVersion>.*:\1:p' src/Plugin.AdMob/Plugin.AdMob.csproj)
major=${current%%.*}
latest=$(curl -sf https://api.nuget.org/v3-flatcontainer/microsoft.maui.controls/index.json |
jq -r --arg m "$major" '.versions[] | select(startswith($m + ".") and (contains("-") | not))' |
sort -V | tail -n 1)
if [ -z "$current" ] || [ -z "$latest" ]; then
echo "::error::Could not determine MAUI versions (current='$current', latest='$latest')"
exit 1
fi
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "latest=$latest" >> "$GITHUB_OUTPUT"
if [ "$latest" != "$current" ] && [ "$(printf '%s\n%s\n' "$current" "$latest" | sort -V | tail -n 1)" = "$latest" ]; then
echo "bump=true" >> "$GITHUB_OUTPUT"
echo "MAUI $current -> $latest"
else
echo "bump=false" >> "$GITHUB_OUTPUT"
echo "MAUI $current is up to date"
fi
prepare:
needs: check
if: needs.check.outputs.bump == 'true'
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
# The candidate goes to a THROWAWAY branch, never straight to the reviewed branch. The
# reviewed branch is only moved once the gate is green (see open-pr), so a failing bump
# can't silently replace the head of an already-open, already-validated PR.
- name: Apply the version bump and push a validation branch
id: push
env:
LATEST: ${{ needs.check.outputs.latest }}
run: |
sed -i -E "s|<MauiPackageVersion>[^<]+</MauiPackageVersion>|<MauiPackageVersion>$LATEST</MauiPackageVersion>|" \
src/Plugin.AdMob/Plugin.AdMob.csproj
sed -i -E \
-e "s|(Include=\"Microsoft\.Maui\.Controls\" Version=\")[^\"]+|\1$LATEST|" \
-e "s|(Include=\"Microsoft\.Maui\.Controls\.Compatibility\" Version=\")[^\"]+|\1$LATEST|" \
-e "s|(Include=\"Microsoft\.AspNetCore\.Components\.WebView\.Maui\" Version=\")[^\"]+|\1$LATEST|" \
samples/Foo.Bar.SampleApp/Foo.Bar.SampleApp.csproj \
samples/Foo.Bar.SampleApp.Hybrid/Foo.Bar.SampleApp.Hybrid.csproj
git diff --stat
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B bump-maui-validate
git commit -am "Bump .NET MAUI to $LATEST"
git push --force origin bump-maui-validate
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
# Hard gate: the bumped source must restore + compile on every target framework.
validate-build:
needs: [check, prepare]
if: needs.check.outputs.bump == 'true'
uses: ./.github/workflows/build.yml
with:
ref: bump-maui-validate
# Runtime signal: device tests against the bumped SOURCE. Advisory for opening the PR
# (its result is reported in the PR body) so emulator flakiness can't block a valid bump.
validate-device:
needs: [check, prepare]
if: needs.check.outputs.bump == 'true'
uses: ./.github/workflows/device-tests.yml
with:
ref: bump-maui-validate
use_published: false
# Pin MAUI to the version being bumped to — otherwise the harness's floating
# Microsoft.Maui.Controls reference would test a different MAUI than the PR ships.
maui_version: ${{ needs.check.outputs.latest }}
open-pr:
needs: [check, prepare, validate-build, validate-device]
if: always() && needs.check.outputs.bump == 'true' && needs.validate-build.result == 'success'
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
ref: bump-maui-validate
fetch-depth: 0 # need real history to tell bot commits from human ones
# Move the reviewed branch onto the validated commit. Head, title and body therefore
# always change together — the atomicity peter-evans/create-pull-request used to give us.
- name: Promote the validated commit onto the review branch
id: promote
run: |
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
git fetch --no-tags origin +refs/heads/bump-maui-version:refs/remotes/origin/bump-maui-version || true
if ! git rev-parse -q --verify refs/remotes/origin/bump-maui-version >/dev/null; then
git push origin HEAD:refs/heads/bump-maui-version
echo "pushed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
# Already holds exactly this change (open PR, re-run on a later night): leave it alone.
if [ "$(git rev-parse refs/remotes/origin/bump-maui-version^{tree})" = "$(git rev-parse HEAD^{tree})" ]; then
echo "bump-maui-version already holds this exact change; nothing to push."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Only inspect commits unique to the branch, so main's own human history is ignored.
if git log --format=%ae refs/remotes/origin/main..refs/remotes/origin/bump-maui-version |
grep -qv '^41898282+github-actions\[bot\]@users\.noreply\.github\.com$'; then
echo "::warning::bump-maui-version carries manual commits — skipping the bump rather than clobbering them."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git push --force-with-lease=bump-maui-version:"$(git rev-parse refs/remotes/origin/bump-maui-version)" \
origin HEAD:refs/heads/bump-maui-version
echo "pushed=true" >> "$GITHUB_OUTPUT"
- name: Open or update the pull request
if: steps.promote.outputs.pushed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURRENT: ${{ needs.check.outputs.current }}
LATEST: ${{ needs.check.outputs.latest }}
BUILD_RESULT: ${{ needs.validate-build.result }}
DEVICE_RESULT: ${{ needs.validate-device.result }}
run: |
body=$(printf '%s\n' \
"Bumps .NET MAUI from \`$CURRENT\` to \`$LATEST\`." \
"" \
"Updates \`MauiPackageVersion\` in the plugin (which also becomes the next plugin package version) and the pinned MAUI packages in both sample apps." \
"" \
"**Pre-merge validation on this exact commit:**" \
"- Build gate (all target frameworks): $BUILD_RESULT" \
"- Device tests (Android emulator, banner hard-gate): $DEVICE_RESULT" \
"" \
"Release notes: https://github.com/dotnet/maui/releases")
existing=$(gh pr list --head bump-maui-version --state open --json number -q '.[0].number' || true)
if [ -n "$existing" ]; then
echo "PR #$existing already open; branch push updated it."
gh pr edit "$existing" --title "Bump .NET MAUI to $LATEST" --body "$body"
else
gh pr create --base main --head bump-maui-version --title "Bump .NET MAUI to $LATEST" --body "$body"
fi
- name: Clean up the validation branch
if: always()
run: git push origin --delete bump-maui-validate || true