Purpose
- Propagate a change from a source PR in an Azure Functions AZD sample to related AZD Gallery templates (Functions service templates) via batch PRs.
- Ensure user-in-the-loop validation of targets and dry-run previews before creating PRs.
Scope
- Source PR: URL provided by user at runtime (example: Azure-Samples/functions-quickstart-dotnet-azd#18).
- Target repositories: AZD Gallery templates filtered as:
- Service type: Functions service templates
- Author: Microsoft* OR paulyuk
- Operations: Discover targets, confirm with user, create branches, apply equivalent change, open PRs, and report results.
Prerequisites
- MCP servers configured and reachable:
- AZD Template discovery server (e.g., mcp-azd-template) with capability to search/filter AZD Gallery templates by service type and author. Repo: https://github.com/spboyer/mcp-azd-template. Start it with:
npx -y mcp-azd-template@latest
, which registers in the IDE as the MCP serverazd-template-helper
. - GitHub MCP with permissions to read repos, fork/create branches (if needed), and open pull requests in the target repositories.
- AZD Template discovery server (e.g., mcp-azd-template) with capability to search/filter AZD Gallery templates by service type and author. Repo: https://github.com/spboyer/mcp-azd-template. Start it with:
- Auth:
- GitHub access token with repo scope for relevant orgs/repos.
- Local environment optional for dry-run diffs (cloning): git available.
Inputs (at runtime)
- SOURCE_PR_URL: The source PR to propagate.
- FILTERS:
- serviceType = "Functions"
- authors = ["Microsoft", "paulyuk"] (match by owner or author metadata)
- optional: languages (e.g., dotnet, node, python, java), triggers (http, queue, timer, blob, service bus, event grid, etc.)
- EXECUTION_MODE:
- discover-only | dry-run | create-prs
- BATCH_LIMIT: Maximum PRs to create in a single run (default 10).
High-level Workflow
- Read and analyze SOURCE_PR_URL
- Fetch PR metadata: title, body, state, commits, changed files, labels, linked issues.
- Retrieve patch/diff for changed files.
- Summarize the intent of change and categorize file types (code, infra, docs, CI, azd metadata).
- Discover candidate target templates
- Query AZD Gallery templates with filters: serviceType=Functions and author in {Microsoft, paulyuk}.
- Collect repository metadata (name, owner, default branch, languages, triggers/tags).
- Present the list to the user for validation before proceeding.
- User validation checkpoint (REQUIRED)
- Show a paginated list with repo name, owner, template description, language, trigger(s).
- Allow the user to:
- Approve all, or
- Deselect specific repos, or
- Edit filters and re-run discovery.
- Prepare change mapping
- Infer change type per file from source PR (e.g., csproj flag, host.json setting, infra main.bicep tweak, README section, CI matrix changes).
- Define cross-language/trigger mapping rules if applicable. Examples:
- host.json or local.settings.json: similar location across languages; adjust syntax if needed.
- infra/ (Bicep/Terraform): mirror property names; verify API versions.
- README: propagate wording consistently; keep language-specific code blocks.
- CI/CD: adapt job names, paths, and language tooling versions.
- If exact mapping is ambiguous, mark repo as “manual review needed” and skip for auto PR.
- Dry-run (optional but recommended)
- For each approved target repo up to BATCH_LIMIT:
- Clone or fetch a shallow copy of the default branch.
- Create a working branch: chore/propagate-from--pr-.
- Apply mapped changes to corresponding files.
- Produce a diff summary and file list; do not push.
- Present consolidated dry-run results to user for final approval.
- For each approved target repo up to BATCH_LIMIT:
- Create PRs
- For each target in scope (after approval):
- Ensure repository permissions and branch protection rules are respected.
- (Fork Mode) If you lack direct push permission to Azure-Samples/:
- Ensure a fork exists under the user account (e.g., paulyuk/); create one if absent.
- Sync fork default branch with upstream (fast-forward) before creating the feature branch.
- Create / update feature branch in the fork and push changes there.
- Open a pull request with base: Azure-Samples/@main and head: paulyuk:.
- (Direct Mode) If you have push permission, create/update the feature branch in the upstream and open the PR normally.
- Commit with a standardized message.
- Open a pull request targeting the upstream default branch (always Azure-Samples main as base for consistency).
- Respect rate limits and backoff; stop at BATCH_LIMIT in a single run.
- For each target in scope (after approval):
- Reporting
- Summarize successes (PR URLs) and failures with reasons.
- Provide a CSV/JSON artifact of results for tracking.
Standardized Artifacts
- Branch name
- chore/propagate-from--pr-
- Commit message
-
chore: propagate changes from #
Source PR: <SOURCE_PR_URL> Summary: Notes: Applied equivalent updates across AZD Functions templates.
-
- Pull request title
- Propagate:
(from #)
- Propagate:
- Pull request body (template)
-
This PR propagates changes from <SOURCE_PR_URL> into this AZD Functions template.
Summary of upstream change:
Applied updates here:
Validation:
- Build/CI passes
- Local azd provision/deploy (optional)
Notes:
- Generated via MCP batch propagation workflow.
-
Safety and Guardrails
- Do not proceed beyond discovery without explicit user approval.
- Skip repositories where mapping is ambiguous or conflicting.
- Respect existing open PRs touching the same files: add comments or update instead of duplicating.
- Obey CODEOWNERS and required labels when creating PRs.
- BATCH_LIMIT to avoid spamming.
- Add a label: area:automation or propagation-bot if available.
Error Handling & Retries
- If cloning fails: retry once with backoff; otherwise skip and log.
- If PR creation fails due to permissions: report and continue.
- If merge conflicts occur: convert to draft PR and note manual intervention required.
Execution Modes
- discover-only: Only list candidate repos for approval.
- dry-run: Prepare local diffs and present a preview without pushing.
- create-prs: Execute branch creation and PR opening after approval.
User Interaction Checkpoints
- After discovery: show target list for approval/edit.
- After dry-run: show diff summaries for approval.
- After PR creation: show results and links.
Reusable Prompt Blocks (copy/paste into your MCP orchestrator)
Prompt: Discover AZD Functions templates
-
Goal: Return AZD Gallery templates filtered by Functions service type and authors (Microsoft* or paulyuk)
-
Tooling: AZD Template discovery server (mcp-azd-template)
-
Variables:
- SOURCE_PR_URL:
- AUTHORS: ["Microsoft", "paulyuk"]
- SERVICE_TYPE: "Functions"
- LANGUAGES: [] (optional)
- TRIGGERS: [] (optional)
- LIMIT: <int, optional>
-
Instruction: """ Discover AZD Gallery templates using these filters:
- serviceType: {SERVICE_TYPE}
- authors: {AUTHORS}
- languages: {LANGUAGES}
- triggers: {TRIGGERS}
Return a JSON array of candidates with fields: [ { "name": string, "owner": string, "repo": string, // owner/repo "description": string, "defaultBranch": string, "languages": string[], "triggers": string[], "tags": string[], "url": string } ]
Do not proceed beyond discovery. No side effects. """
Prompt: Dry-run mapping and diff preview
- Goal: For approved targets, compute equivalent changes from SOURCE_PR_URL and produce diffs without pushing
- Tooling: GitHub MCP (read-only clone/fetch), optional local git
- Variables:
- SOURCE_PR_URL:
- TARGETS: ["owner/repo", ...] (approved subset)
- BRANCH_NAME: chore/propagate-from--pr-
- LIMIT:
- Instruction:
"""
- Fetch SOURCE_PR_URL metadata and unified diff.
- For each TARGET in TARGETS (up to LIMIT):
- Shallow clone default branch.
- Analyze mapping for each changed file (code/infra/docs/CI). If ambiguous, mark target as manual_review_required and SKIP.
- Apply changes in a temporary working tree.
- Produce a diff summary: { "repo": "owner/repo", "manual_review_required": boolean, "files": [ { "path": string, "changeSummary": string } ], "diff": string // patch text }
- Return an array of these results. Do not push or open PRs. """
Prompt: Create PRs
- Goal: Create branches, commit changes, and open PRs with standardized messages
- Tooling: GitHub MCP (write)
- Variables:
- SOURCE_PR_URL:
- TARGETS: ["owner/repo", ...] (approved subset)
- BRANCH_NAME: chore/propagate-from--pr-
- PR_TITLE_TEMPLATE: "Propagate:
(from #)" - PR_BODY_TEMPLATE: see Standardized Artifacts
- LABELS: ["propagation", "automation"] (if allowed)
- LIMIT:
- FORK_USER: paulyuk (the user account to host forks when direct push is unavailable)
- Instruction:
"""
Using SOURCE_PR_URL metadata and previously computed mappings:
- For each TARGET (up to LIMIT):
- Check if write permission to upstream (Azure-Samples/) exists.
- If not: ensure fork FORK_USER/ exists (create if missing) and is synchronized with upstream default branch.
- Create or update branch {BRANCH_NAME} in the fork (or upstream if direct write allowed).
- Commit changes with message: "chore: propagate changes from #\n\nSource PR: <SOURCE_PR_URL>\nSummary: \nNotes: Applied equivalent updates across AZD Functions templates."
- Open a PR with: base: Azure-Samples/@defaultBranch head: (FORK_USER or upstream owner):{BRANCH_NAME} using PR_TITLE_TEMPLATE and PR_BODY_TEMPLATE.
- Apply LABELS if permissions allow.
- If conflicts arise, open as draft and note manual intervention.
- Check if write permission to upstream (Azure-Samples/) exists.
- Return JSON results: [ { "repo": "owner/repo", "prUrl": string, "branch": string, "status": "created"|"skipped"|"failed", "reason"?: string } ] """
- For each TARGET (up to LIMIT):
Fork Workflow Variant (Summary)
- Purpose: Operate safely without upstream write access by using user forks (paulyuk) as the PR head source.
- Steps per repo:
- Fork existence check / creation.
- Upstream default branch fetch & fork sync (fast-forward only; if diverged, create a sync commit in fork).
- Feature branch creation in fork: {BRANCH_NAME}.
- Apply mapped changes & commit.
- Open PR: base=Azure-Samples/:main head=paulyuk:{BRANCH_NAME}.
- Apply labels (best-effort) and add propagation run ID comment.
- Record result.
Git CLI Reference (optional manual fallback)
upstream=Azure-Samples/functions-quickstart-javascript-azd
fork_user=paulyuk
branch=chore/propagate-from-functions-quickstart-dotnet-azd-pr-18
git clone https://github.com/$fork_user/${upstream##*/}.git || git clone https://github.com/$upstream.git
cd ${upstream##*/}
git remote add upstream https://github.com/$upstream.git 2>/dev/null || true
git fetch upstream main
git checkout -B main upstream/main
git push origin main
git checkout -B $branch
# (apply edits)
git commit -am "chore: propagate changes from functions-quickstart-dotnet-azd#18"
git push -u origin $branch
gh pr create --base main --head $fork_user:$branch --title "Propagate: <title> (from functions-quickstart-dotnet-azd#18)" --body-file pr_body.md
Output Contracts
- discovery.results.json [ { name, owner, repo, description, defaultBranch, languages, triggers, tags, url } ]
- dryrun.results.json [ { repo, manual_review_required, files: [ { path, changeSummary } ], diff } ]
- createprs.results.json [ { repo, prUrl, branch, status, reason? } ]
Consent Gates
- Gate 1: After discovery.results.json, require explicit user approval and allow deselection.
- Gate 2: After dryrun.results.json, require explicit user approval before PR creation.
- Gate 3: After createprs.results.json, present summary and next steps.
Configuration Parameters
- filters.serviceType: "Functions"
- filters.authors: ["Microsoft", "paulyuk"]
- filters.languages: [optional]
- filters.triggers: [optional]
- batch.limit: default 10
- branchPrefix: chore/propagate-from
- labels: ["propagation", "automation"] (if allowed)
Minimal Pseudo-Flow
- getSourcePr(SOURCE_PR_URL) -> {title, body, files[], patch[]}
- discoverTemplates({serviceType:"Functions", authors:["Microsoft","paulyuk"], languages?, triggers?}) -> repos[]
- promptUserApprove(repos)
- if dry-run:
- for repo in approved[:BATCH_LIMIT]:
- planChanges = mapChanges(sourcePatch, repo)
- diffPreview = applyInMemory(planChanges)
- promptUserApprove(diffPreviews)
- for repo in approved[:BATCH_LIMIT]:
- if create-prs:
- for repo in approved[:BATCH_LIMIT]:
- branch = createBranch(repo, branchName)
- applyChanges(repo, plan)
- commitAndPush(repo, branch)
- openPR(repo, branch, title, body)
- reportSummary()
- for repo in approved[:BATCH_LIMIT]:
Acceptance Criteria
- Shows a filtered list of AZD Functions templates by Microsoft or paulyuk for user confirmation.
- Supports a dry-run with clear diff previews before creating PRs.
- Creates PRs with standardized naming and links back to the source PR.
- Produces a success/failure summary.
Appendix A: Mapping Guidance Examples
- host.json changes: replicate equivalent settings across languages; ensure schema correctness.
- Bicep (infra): verify apiVersion compatibility; update outputs/params consistently; run a linter if available.
- README: keep language-specific commands/tools accurate (dotnet/npm/pip/gradle).
- CI workflows: align tooling versions; matrix may differ per language.
Appendix B: Rate Limiting & Idempotency
- Include a unique propagation run ID in PR body comments.
- Detect and update existing PRs from the same run.
Appendix C: Consent Gates
- The workflow must pause and await user confirmation at discovery and dry-run stages before proceeding to PR creation.