From 34a325319ad6bd8ee23b01d06d471631c9e79723 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:03:23 -0500 Subject: [PATCH 1/3] fix: Ignore merge commits in Integration Data Updater branch guard Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-integration-data.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml index 2a017b333b..1ad89d38b7 100644 --- a/.github/workflows/update-integration-data.yml +++ b/.github/workflows/update-integration-data.yml @@ -107,11 +107,14 @@ jobs: bot_email="aspire-repo-bot[bot]@users.noreply.github.com" # Refuse to replace a rolling branch that contains maintainer changes. + # Merge commits are ignored: clicking "Update branch" on the PR adds a + # human-authored merge of main, which carries no maintainer changes and + # would otherwise block every later run until someone force-pushed. # The exact lease below also protects commits pushed after this check. remote_tip=$(git ls-remote --heads origin "refs/heads/$branch" | awk '{print $1}') if [ -n "$remote_tip" ]; then git fetch --no-tags origin "refs/heads/$branch:refs/remotes/origin/$branch" - non_bot_commits=$(git log --format='%H %ae %ce' "origin/main..origin/$branch" | + non_bot_commits=$(git log --no-merges --format='%H %ae %ce' "origin/main..origin/$branch" | awk -v bot="$bot_email" '$2 != bot || $3 != bot') if [ -n "$non_bot_commits" ]; then echo "::error::Refusing to replace $branch because it contains non-bot commits:" From 67f8677fdca1a6bac269d8af6234c0f32d54ed42 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:08:42 -0500 Subject: [PATCH 2/3] fix: Always refresh the rolling Integration Data Updater branch Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-integration-data.yml | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml index 1ad89d38b7..8180a9765d 100644 --- a/.github/workflows/update-integration-data.yml +++ b/.github/workflows/update-integration-data.yml @@ -104,24 +104,6 @@ jobs: # a new branch) every day. The rolling commit is always a single snapshot # rebased onto current main, which also keeps the PR conflict-free. branch="chore/update-integration-data" - bot_email="aspire-repo-bot[bot]@users.noreply.github.com" - - # Refuse to replace a rolling branch that contains maintainer changes. - # Merge commits are ignored: clicking "Update branch" on the PR adds a - # human-authored merge of main, which carries no maintainer changes and - # would otherwise block every later run until someone force-pushed. - # The exact lease below also protects commits pushed after this check. - remote_tip=$(git ls-remote --heads origin "refs/heads/$branch" | awk '{print $1}') - if [ -n "$remote_tip" ]; then - git fetch --no-tags origin "refs/heads/$branch:refs/remotes/origin/$branch" - non_bot_commits=$(git log --no-merges --format='%H %ae %ce' "origin/main..origin/$branch" | - awk -v bot="$bot_email" '$2 != bot || $3 != bot') - if [ -n "$non_bot_commits" ]; then - echo "::error::Refusing to replace $branch because it contains non-bot commits:" - echo "$non_bot_commits" - exit 1 - fi - fi # Stage only the allowed generated data files — never workflow files, # source, manifests, or docs. Directories with no changes are no-ops. @@ -149,11 +131,7 @@ jobs: git commit \ -m "$PR_TITLE" \ -m "Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>" - if [ -n "$remote_tip" ]; then - git push --force-with-lease="refs/heads/$branch:$remote_tip" origin "HEAD:refs/heads/$branch" - else - git push origin "HEAD:refs/heads/$branch" - fi + git push --force origin "HEAD:refs/heads/$branch" echo "branch=$branch" >> "$GITHUB_OUTPUT" echo "pushed=true" >> "$GITHUB_OUTPUT" From d48a417121f92f69b59dd6671b16c65f69fed467 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:30:11 -0500 Subject: [PATCH 3/3] Simplify Integration Data Updater and auto-merge on green CI Add .github/policies/auto-merge.yml. When the Aspire bot labels a PR ":octocat: auto-merge", the GitOps policy approves it and enables squash auto-merge, so it merges once the required ci-gate check passes. Removing the label disables auto-merge. The sender is limited to aspire-repo-bot[bot] because the main ruleset requires one approval. Simplify the workflow now that nobody commits to the bot branch: - Push with a bare --force-with-lease instead of --force, so the push fails if the branch moved since checkout. - Set the git identity in the commit step instead of a separate step. - Remove the stray-PR cleanup for the legacy timestamped branches. Update the PR body, console warning and update-integrations skill to say the PR auto-merges and that removing the label holds it for review. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 510be1dc-27eb-4efc-a731-1f0065f67252 --- .agents/skills/update-integrations/SKILL.md | 2 +- .github/policies/auto-merge.yml | 40 +++++++++++++++++ .github/workflows/update-integration-data.yml | 43 +++++++------------ .../scripts/update-integration-data.ps1 | 8 ++-- 4 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 .github/policies/auto-merge.yml diff --git a/.agents/skills/update-integrations/SKILL.md b/.agents/skills/update-integrations/SKILL.md index 4fff347d8f..c76c33f08d 100644 --- a/.agents/skills/update-integrations/SKILL.md +++ b/.agents/skills/update-integrations/SKILL.md @@ -7,7 +7,7 @@ description: Update integration documentation links and API reference data by sy This skill synchronizes the integration package catalog with documentation URL mappings and generates per-package API reference schemas. It ensures every NuGet package listed in the integrations data file has a corresponding documentation link and an up-to-date API reference JSON file. -> **Automation note:** The scheduled `update-integration-data` workflow (`.github/workflows/update-integration-data.yml`) runs this update deterministically — no agent/LLM. It executes `src/frontend/scripts/update-integration-data.ps1`, which runs `pnpm update:all`, detects `"version":` changes in `src/frontend/src/data/aspire-integrations.json`, and — only when a version actually moves — runs the C# and TypeScript API regeneration (and the twoslash bundle) before opening a PR via the Aspire bot GitHub App. You can reproduce the full flow locally with `pwsh src/frontend/scripts/update-integration-data.ps1` (add `-SkipRegen` for a fast data-only run). Use this skill for **manual or one-off runs that need judgment** — for example: adding a new integration, reconciling `integration-docs.json`, validating release-branch feed resolution, troubleshooting a regen failure flagged by the workflow's PR, or regenerating data outside the scheduled cadence. +> **Automation note:** The scheduled `update-integration-data` workflow (`.github/workflows/update-integration-data.yml`) runs this update deterministically — no agent/LLM. It executes `src/frontend/scripts/update-integration-data.ps1`, which runs `pnpm update:all`, detects `"version":` changes in `src/frontend/src/data/aspire-integrations.json`, and — only when a version actually moves — runs the C# and TypeScript API regeneration (and the twoslash bundle) before opening or refreshing a PR via the Aspire bot GitHub App. That PR is approved and squash-merged automatically by `.github/policies/auto-merge.yml` once the required `ci-gate` check passes; removing its `:octocat: auto-merge` label holds it for manual review. You can reproduce the full flow locally with `pwsh src/frontend/scripts/update-integration-data.ps1` (add `-SkipRegen` for a fast data-only run). Use this skill for **manual or one-off runs that need judgment** — for example: adding a new integration, reconciling `integration-docs.json`, validating release-branch feed resolution, troubleshooting a regen failure flagged by the workflow's PR, or regenerating data outside the scheduled cadence. ## Overview diff --git a/.github/policies/auto-merge.yml b/.github/policies/auto-merge.yml new file mode 100644 index 0000000000..6922d7457b --- /dev/null +++ b/.github/policies/auto-merge.yml @@ -0,0 +1,40 @@ +id: auto.merge +name: GitOps.PullRequestIssueManagement +description: Auto-merge the Integration Data Updater pull request after the required ci-gate check passes +owner: +resource: repository +disabled: false +where: +configuration: + resourceManagementConfiguration: + eventResponderTasks: + + - description: >- + Approve and auto-squash-merge PRs to main labeled ':octocat: auto-merge'. + The label is only applied by the Aspire bot GitHub App when + update-integration-data.yml opens its PR. The sender check keeps + anyone else from self-approving a PR by adding the label, because + branch protection requires one approval. Branch protection also + requires ci-gate to pass on the PR branch without requiring it to be + up to date with main before merging. + if: + - payloadType: Pull_Request + - labelAdded: + label: ':octocat: auto-merge' + - targetsBranch: + branch: main + - isActivitySender: + user: aspire-repo-bot[bot] + then: + - enableAutoMerge: + mergeMethod: Squash + - approvePullRequest: + comment: "Approved; this PR will merge after the required ci-gate check passes." + + - description: Disable auto-merge when the auto-merge label is removed + if: + - payloadType: Pull_Request + - labelRemoved: + label: ':octocat: auto-merge' + then: + - disableAutoMerge diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml index 5ac62e8e4c..29290a2d03 100644 --- a/.github/workflows/update-integration-data.yml +++ b/.github/workflows/update-integration-data.yml @@ -4,6 +4,12 @@ name: Integration Data Updater # update scripts directly (no LLM inference, no agent timeout, no PAT auth) and # opens a PR through the Aspire bot GitHub App when the generated data changes. # +# The bot owns the rolling `chore/update-integration-data` branch and force-pushes +# it on every run, so nobody should commit to it. The PR is labeled +# `:octocat: auto-merge`, which .github/policies/auto-merge.yml turns into an +# approval and a squash auto-merge once the required `ci-gate` check passes. +# Remove the label to hold a PR for manual review. +# # All logic lives in src/frontend/scripts/update-integration-data.ps1 so the # exact same flow can be reproduced locally: # pwsh src/frontend/scripts/update-integration-data.ps1 @@ -84,13 +90,6 @@ jobs: GITHUB_TOKEN: ${{ github.token }} run: ./src/frontend/scripts/update-integration-data.ps1 - - name: Configure git identity - if: ${{ steps.update.outputs.changed == 'true' }} - shell: bash - run: | - git config user.name "aspire-repo-bot[bot]" - git config user.email "aspire-repo-bot[bot]@users.noreply.github.com" - - name: Commit and refresh automation branch if: ${{ steps.update.outputs.changed == 'true' }} id: push @@ -101,9 +100,9 @@ jobs: set -euo pipefail # Single long-lived automation branch. Each run force-pushes one commit - # with the latest generated data on top of the freshest main, so an - # unreviewed PR is refreshed in place instead of piling up a new PR (and - # a new branch) every day. The rolling commit is always a single snapshot + # with the latest generated data on top of the freshest main, so the + # open PR is refreshed in place instead of piling up a new PR (and a new + # branch) every day. The rolling commit is always a single snapshot # rebased onto current main, which also keeps the PR conflict-free. branch="chore/update-integration-data" @@ -129,12 +128,16 @@ jobs: fi # Reset the branch to a single fresh commit on top of the current main - # and force-push, so the open PR (if any) updates in place. + # and force-push, so the open PR (if any) updates in place. The lease + # fails the push if the branch moved since checkout instead of + # overwriting it. + git config user.name "aspire-repo-bot[bot]" + git config user.email "aspire-repo-bot[bot]@users.noreply.github.com" git checkout -B "$branch" git commit \ -m "$PR_TITLE" \ -m "Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>" - git push --force origin "HEAD:refs/heads/$branch" + git push --force-with-lease origin "HEAD:refs/heads/$branch" echo "branch=$branch" >> "$GITHUB_OUTPUT" echo "pushed=true" >> "$GITHUB_OUTPUT" @@ -152,6 +155,7 @@ jobs: # Reuse the existing open PR for the rolling branch if there is one, so # its review threads and history carry over; otherwise open a fresh PR. + # Adding the label at creation is what triggers the auto-merge policy. existing=$(gh pr list \ --state open \ --head "$BRANCH" \ @@ -173,21 +177,6 @@ jobs: echo "Created $new_pr" fi - # Close any stray older automation PRs (e.g. legacy timestamped branches - # from before the rolling-branch model) so only the single PR remains. - strays=$(gh pr list \ - --state open \ - --limit 30 \ - --json number,headRefName \ - --jq '.[] | select(.headRefName | startswith("chore/update-integration-data-")) | select(.headRefName != env.BRANCH) | .number') - - for pr in $strays; do - echo "Closing stray automation PR #$pr" - gh pr close "$pr" \ - --delete-branch \ - --comment "Superseded by the rolling Integration Data Updater PR." - done - - name: Summary if: ${{ always() }} shell: bash diff --git a/src/frontend/scripts/update-integration-data.ps1 b/src/frontend/scripts/update-integration-data.ps1 index 2085226bdf..754f8c83c3 100644 --- a/src/frontend/scripts/update-integration-data.ps1 +++ b/src/frontend/scripts/update-integration-data.ps1 @@ -504,7 +504,7 @@ if ($versionsChanged -and -not $SkipRegen) { } if ($tsToleratedPackages) { $toleratedWarning = "TypeScript modules were generated despite known ATS scanner diagnostics for: $tsToleratedPackages. " + - "Their APIs may be incomplete until the Aspire CLI includes microsoft/aspire#20443; review them before merging." + "Their APIs may be incomplete until the Aspire CLI includes microsoft/aspire#20443. The automation PR auto-merges once CI passes. Remove its auto-merge label to review these modules first." Write-Warning $toleratedWarning foreach ($diagnostic in $tsToleratedDiagnostics) { Write-Warning " $diagnostic" @@ -617,6 +617,8 @@ $sb = [System.Text.StringBuilder]::new() [void]$sb.AppendLine("") [void]$sb.AppendLine("Generated deterministically by ``src/frontend/scripts/update-integration-data.ps1`` (no agent inference).") [void]$sb.AppendLine("") +[void]$sb.AppendLine("This PR is approved and squash-merged automatically once ``ci-gate`` passes, as configured in ``.github/policies/auto-merge.yml``. Remove the ``:octocat: auto-merge`` label to hold it for manual review.") +[void]$sb.AppendLine("") [void]$sb.AppendLine("### What's updated") [void]$sb.AppendLine("- ``src/frontend/src/data/aspire-integrations.json`` — latest package information") [void]$sb.AppendLine("- ``src/frontend/src/data/integration-docs.json`` — documentation mappings, when packages are removed") @@ -664,7 +666,7 @@ if ($regenRan) { [void]$sb.AppendLine("") [void]$sb.AppendLine("### :warning: Tolerated ATS scanner diagnostics") [void]$sb.AppendLine("") - [void]$sb.AppendLine("The Aspire CLI reported known duplicate inherited-capability diagnostics for the packages below. Their TypeScript modules were generated anyway, so the APIs named in these diagnostics may be incomplete until the CLI includes the scanner fix from microsoft/aspire#20443. **Review these modules before merging.**") + [void]$sb.AppendLine("The Aspire CLI reported known duplicate inherited-capability diagnostics for the packages below. Their TypeScript modules were generated anyway, so the APIs named in these diagnostics may be incomplete until the CLI includes the scanner fix from microsoft/aspire#20443. **Remove the ``:octocat: auto-merge`` label to review these modules before this PR merges.**") [void]$sb.AppendLine("") foreach ($packageName in $tsToleratedPackages -split ',\s*') { [void]$sb.AppendLine("- ``$packageName``") @@ -694,7 +696,7 @@ if ($iconWarnings) { [void]$sb.AppendLine("``$iconWarnings``") [void]$sb.AppendLine("") } -[void]$sb.AppendLine("### Review checklist") +[void]$sb.AppendLine("### Manual review checklist") [void]$sb.AppendLine("") [void]$sb.AppendLine("- [ ] Integration data looks reasonable (no suspicious anomalies)") [void]$sb.AppendLine("- [ ] Package counts and versions updated appropriately")