Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/skills/azsdk-common-patch-mergeback/SKILL.md

@vcolin7 vcolin7 Jul 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just out of curiosity, have you invoked the skill locally yourself? Are we planning to use this inside an agentic-workflow or will it only be manually triggered by us?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I used the skill to create this PR. The end goal is for this to be used within an agentic workflow, but I have to iron out the issues first

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: azsdk-common-patch-mergeback
license: MIT
metadata:
version: "1.0.0"
distribution: shared
description: 'Prepare a merge-back PR that brings patch-release version, CHANGELOG, and pom.xml updates from a `release/patch/YYYYMMDD` branch back into `main`. **WORKFLOW SKILL**. USE FOR: "merge-back PR", "merge back patches", "patch release merge-back", "bring patch releases into main", "reconcile release/patch branch with main". DO NOT USE FOR: triggering SDK releases, incrementing versions for a new patch, general SDK code generation. INVOKES: eng/versioning/update_versions.py.'
compatibility: "Requires a checked-out azure-sdk-for-java repo with git access to the release/patch branch and Python installed for eng/versioning/update_versions.py."
---

# Patch Release Merge-back

Prepare a PR that merges the version, CHANGELOG, and downstream `pom.xml`
updates produced by a patch release (on a `release/patch/YYYYMMDD` branch) back
into `main`. Patches revert `CHANGELOG.md` and version files to the last stable
release, so a naive merge creates many conflicts. This skill encodes how to
resolve each file type correctly.

## Triggers

WHEN: "merge-back PR", "merge back patches", "patch release merge-back", "bring patch releases into main", "reconcile the patch branch"
DO NOT USE FOR: triggering an SDK release, incrementing versions for a new patch cycle, code generation.

## Inputs (variables)

Two values drive the whole workflow. Both come from the **"Increment versions
for patch releases"** PR (opened by `azure-sdk-automation[bot]`) that targets the
patch branch:

| Variable | How to determine it | Current value |
| ---------------- | ------------------------------------------------------------------------------- | ----------------------- |
| `RELEASE_BRANCH` | The branch the "Increment versions" PR targets: `release/patch/YYYYMMDD`. | `release/patch/20260701` |
| `PATCH_DATE` | Derived from the branch name `YYYYMMDD` → `YYYY-MM-DD`. Used in CHANGELOG entries. | `2026-07-01` |

If either value is ambiguous, ask the user to confirm before proceeding.

## Critical Rules (read first)

- **Only the last two commits** of `RELEASE_BRANCH` carry the updates to bring
back. Diff against `RELEASE_BRANCH~2` to scope the change set.
- **Never edit `pom.xml` by hand.** It is regenerated by
`eng/versioning/update_versions.py` **after** `version_client.txt` is correct.
- **Do not touch `README.md` files.** Run the version script with `--skip-readme`
so READMEs are left unchanged (README updates are handled separately).
- **`version_client.txt`:** update only the `dependency-version` of SDK
libraries that changed on the release branch; **always keep the
`current-version` from `main`.** Never reset beta versions to `beta.1`.
Comment on lines +45 to +47

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment on having a newer stable version on main than the one on the patch branch.

- **`CHANGELOG.md`:** keep the `PATCH_DATE` entry from the release branch; every
other line must match `main`. Do not invent or edit any other CHANGELOG
content.
- Base the merge-back branch on **`main`**, not on the release branch.

## Workflow

1. **Confirm inputs** — Establish `RELEASE_BRANCH` and `PATCH_DATE` (see table
above). Fetch the branch: `git fetch origin RELEASE_BRANCH`.
2. **Create the working branch from `main`:**
`git checkout main && git pull` then
`git checkout -b copilot/merge-back-release-patch-YYYYMMDD`.
3. **Scope the changes** — List files touched by the last two commits:
`git diff --name-only origin/RELEASE_BRANCH~2 origin/RELEASE_BRANCH`.
Expect three kinds: `eng/versioning/version_client.txt`, many `CHANGELOG.md`,
and many `pom.xml` (+ possibly `README.md`).
4. **Reconcile `version_client.txt`** — Follow
[references/version-client-resolution.md](references/version-client-resolution.md).
5. **Reconcile each `CHANGELOG.md`** — Follow
[references/changelog-resolution.md](references/changelog-resolution.md).
6. **Regenerate `pom.xml`** — From the repo root run the version script with the
`--skip-readme` (`--sr`) flag so `README.md` files are left untouched:
```bash
python eng/versioning/update_versions.py --skip-readme
```
Do not stage any manual pom.xml edits; only the generated output. Confirm no

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit;

Suggested change
Do not stage any manual pom.xml edits; only the generated output. Confirm no
Do not stage any manual `pom.xml` edits; only the generated output. Confirm no

`README.md` files appear in the resulting diff.
7. **Review & sanity-check** — Verify no v2 (`*-v2`) or unrelated libraries were
touched, no `current-version` values were altered, and no beta version was
reset to `beta.1`.
8. **Commit and open the PR against `main`** — Title it like
`<Month> <Year> Patches Merge-back`. Summarize: bumped dependency versions,
inserted `PATCH_DATE` CHANGELOG entries, and auto-generated pom.xml updates
(READMEs intentionally left unchanged via `--skip-readme`).

## Examples

- "Prepare the merge-back PR for `release/patch/20260701` into main."
- "Bring the July 2026 patch releases back into main."

## Troubleshooting

- **`current-version` mismatch after regeneration** (e.g. `2.59.0-beta.1` vs
`2.59.0-beta.2`): you likely overwrote a `current-version`. Restore it from
`main`; only `dependency-version` should change.
- **Unexpected pom.xml diffs:** re-run `update_versions.py` only after
`version_client.txt` is fully correct; stray diffs usually mean the version
file still has a wrong entry.
- **Accidental v2 library changes:** revert them — patch merge-backs only cover
the patched GA/beta libraries listed in the release branch diff.
- **Wrong CHANGELOG "from" versions** in `## X.Y.Z (PATCH_DATE)` dependency
bullets: correct them to match the actual previous release, per the release
branch entry.
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Reconciling `CHANGELOG.md` files

Patches revert each library's `CHANGELOG.md` to its last stable release, dropping
the `(Unreleased)` section and any newer entries that exist on `main`. The
merge-back must restore `main`'s content **and** insert the patch entry.

## Target structure of the resolved file

Produce, from top to bottom:

1. **The `## X.Y.Z (Unreleased)` section exactly as it is on `main`** — including
any unreleased entries already present there. Do not add, edit, or remove

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
any unreleased entries already present there. Do not add, edit, or remove
any unreleased entries already present there. Do not add to, edit, or remove

unreleased content.
2. **The patch entry from `RELEASE_BRANCH`**, i.e. the `## X.Y.Z (PATCH_DATE)`
section (e.g. `## 12.33.3 (2026-07-01)`). This is the *only* content taken
from the release branch.
3. **The remainder of the file exactly as it is on `main`** (all previously
released entries, unchanged).

Concretely: take the `main` version of the file and **insert the release
branch's `PATCH_DATE` entry immediately below the `(Unreleased)` section.**
Nothing else changes.
Comment on lines +20 to +22

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sometimes our patches happen and then a beta release occurs, leaving us with an empty Unreleased section followed by said newer beta. Maybe we can update the verbiage here to guide the agent to use the reported release dates instead.


## Rules

- The patch entry's heading must carry `PATCH_DATE` (the `YYYY-MM-DD` derived
from the release branch name), not `(Unreleased)`.
- Do not merge, reorder, or de-duplicate other entries.
- Do not modify wording anywhere except inserting the patch block.
- Verify dependency-bump bullets inside the patch entry reference the correct
"from" version. The "from" version is the version of each dependency that was
present in the release that the patch was applied to (i.e., the base version
the patch built on top of) — this is **not** necessarily the most recent
version of that dependency released to Maven. Correct any bullets if the
release branch entry is wrong.

The release-branch patch entry is frequently wrong here: it often copies the
"from" version from an older release instead of the immediately preceding one.
To find the correct "from" version, look at the **immediately preceding
release entry** in that same `CHANGELOG.md` (the next `## X.Y.Z (DATE)` section
below the patch entry, on the release branch, which contains the library's full
history). For each dependency bumped in the patch entry, its correct "from"
version is the **"to" version recorded for that same dependency in the
immediately preceding release entry**. If that preceding entry did not touch
the dependency, walk further back to the most recent release that did.

Concretely, for every dependency-bump bullet in the patch entry, of the form:

```
- Upgraded `dep` from `A` to version `B`.
```

1. Find the most recent release entry below it that also upgraded `dep`, and
read that entry's "to" version `C`.
2. If `A` is not equal to `C`, replace `A` with `C` so the bullet reads
`from` `C` `to version` `B`.

Example — a `2.53.9` patch entry says:

```
- Upgraded `azure-resourcemanager-resources` from `2.54.0` to version `2.54.2`.
```

but the preceding `2.53.8` release entry says:

```
- Upgraded `azure-resourcemanager-resources` from `2.54.0` to version `2.54.1`.
```

Because `2.53.8` already shipped `2.54.1`, the `2.53.9` bullet's "from" must be
corrected to `2.54.1`:

```
- Upgraded `azure-resourcemanager-resources` from `2.54.1` to version `2.54.2`.
```

Apply this check to **every** dependency bullet, since a single patch entry can
list several dependencies each with its own preceding "to" version.

## Example (before → after)

`main` CHANGELOG top:
```
## 12.34.0-beta.1 (Unreleased)

### Features Added

## 12.33.2 (2026-02-15)
...
```

`RELEASE_BRANCH` patch entry:
```
## 12.33.3 (2026-07-01)

### Bugs Fixed
- Fixed ...
```

Resolved file:
```
## 12.34.0-beta.1 (Unreleased)

### Features Added

## 12.33.3 (2026-07-01)

### Bugs Fixed
- Fixed ...

## 12.33.2 (2026-02-15)
...
```

Repeat for every `CHANGELOG.md` in the last-two-commits diff of the release
branch.
Comment on lines +115 to +116

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is assuming we don't have to manually fix anything, right? Have you had instances where you've had to manually intervene to fix conflicts with test recordings or other things? It used to happen to me back in the day every few releases.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, this assumes patches were released without issue. If things need to change then additional edits may need to be included into the Changelog.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Reconciling `eng/versioning/version_client.txt`

## File format

Each SDK line is `<groupId>:<artifactId>;dependency-version;current-version`.
Read the header of [eng/versioning/version_client.txt](../../../../eng/versioning/version_client.txt)
for the authoritative explanation. Key points:

- **`dependency-version`** — what libraries *outside* the release group use as a
Maven dependency. A patch release bumps this to the newly released stable
version.
- **`current-version`** — the in-development version of the library on `main`.
This must **not** change during a merge-back.

## Resolution algorithm

Compare the file on `main` against the file on `RELEASE_BRANCH`, line by line
(match by `groupId:artifactId`):

1. If the release branch line has a **different `dependency-version`** than
`main`, take the release branch `dependency-version`.
Comment on lines +20 to +21

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Assuming the version from the patch is not older than what's on main. In a comment I made on the azure-resourcemanager-network changelog I noted there was a newer release than the one in the patch. If we think there will be no window long enough for a patch to be release followed by another stable version before we merge-back, I'd disregard this comment.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The goal is to automate the process to a point where the patches can be released, the merge-back PR auto-merged, and BOM released in one smooth motion that would prevent any libraries being released in-between.

2. **Always keep the `current-version` from `main`**, regardless of what the
release branch shows (patches revert `current-version` to the stable value).
3. Leave every other line exactly as it is on `main`.

Resulting line = `groupId:artifactId;<release-branch dependency-version>;<main current-version>`.

## Guardrails

- **Do not reset beta versions to `beta.1`.** If a `current-version` on `main`
is e.g. `1.2.0-beta.4`, it stays `1.2.0-beta.4`.
- Only touch SDK library lines whose `dependency-version` actually changed on
the release branch. Do not modify parent/BOM lines or unrelated libraries.
- Never alter `current-version` values — a changed `current-version` is the most
common merge-back bug and surfaces later as a pom.xml mismatch.

## Example

- `main`: `com.azure:azure-storage-blob;12.33.2;12.34.0-beta.1`
- `RELEASE_BRANCH`: `com.azure:azure-storage-blob;12.33.3;12.33.3`
- **Result:** `com.azure:azure-storage-blob;12.33.3;12.34.0-beta.1`

Here the dependency-version moves `12.33.2` → `12.33.3` (the patch), while the
`current-version` stays `12.34.0-beta.1` (from `main`).
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ stages:
displayName: 'Download Signed Artifacts'
artifact: packages-signed

# Setup Maven mirror settings and authenticate with Azure Artifacts
- template: /eng/pipelines/templates/steps/maven-authenticate.yml
parameters:
SourceDirectory: $(Pipeline.Workspace)/azure-sdk-for-java

# gpg-sign and create the flattened directory for ESRP bulk publish
# Note: The maven release requires the files to be local GPG signed
# Dev feed publishes use the gpg-sign-and-deply to do it in one step
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,13 @@ extends:
Paths: $(SparseCheckoutDirectories)
SkipCheckoutNone: true

- task: PipAuthenticate@1
displayName: 'Pip Authenticate to Azure Artifacts'
inputs:
artifactFeeds: 'public/azure-sdk-for-python'

- script: |
python -m pip install markdown2 BeautifulSoup4
python -m pip install markdown2==2.4.6 BeautifulSoup4==4.11.1
displayName: 'pip install markdown2 and BeautifulSoup4'

# Authenticate with Azure Artifacts
Expand Down
Loading
Loading