|
| 1 | +--- |
| 2 | +name: hotfix |
| 3 | +description: Proposes a scoped version bump for a roosterjs hotfix. Given one or more commits provided by the user, determines which packages had files changed by those commits, bumps the version only for the affected version groups, and creates a draft PR. Use when asked to do a hotfix, a scoped/targeted version bump, or to release only specific commits. |
| 4 | +--- |
| 5 | + |
| 6 | +# Hotfix Skill |
| 7 | + |
| 8 | +This skill performs a **scoped** version bump for a roosterjs hotfix. Unlike the full `version-bump` skill (which bumps everything merged into `master` since the last release), this skill bumps the version **only for the packages whose files were changed by the specific commits the user provides**. |
| 9 | + |
| 10 | +The user supplies the commits to hotfix. Each changed file is mapped to its package, each package to a version group (`main`, `legacyAdapter`, `react`), and **only the affected groups are bumped**. |
| 11 | + |
| 12 | +## Inputs |
| 13 | + |
| 14 | +The user must provide the **commits** to include in the hotfix. Accept any of: |
| 15 | + |
| 16 | +- One or more commit hashes (e.g. `5142b51 e593ee8`) |
| 17 | +- A commit range (e.g. `abc123..def456`) |
| 18 | +- PR numbers (resolve to their merge/squash commit with `gh pr view <number> --json mergeCommit`) |
| 19 | + |
| 20 | +If the user does **not** provide any commits, stop and ask which commits should be included in the hotfix before doing anything else. |
| 21 | + |
| 22 | +## Version groups |
| 23 | + |
| 24 | +The version groups live in `versions.json` and map to package sets defined in `tools/buildTools/common.js` (`buildConfig`). Use this mapping to translate changed files → group: |
| 25 | + |
| 26 | +| Group | Packages | |
| 27 | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 28 | +| `main` | roosterjs, roosterjs-content-model-types, roosterjs-content-model-dom, roosterjs-content-model-core, roosterjs-content-model-api, roosterjs-content-model-plugins, roosterjs-color-utils, roosterjs-content-model-markdown | |
| 29 | +| `legacyAdapter` | roosterjs-editor-adapter | |
| 30 | +| `react` | roosterjs-react | |
| 31 | + |
| 32 | +Notes: |
| 33 | + |
| 34 | +- A file path under `packages/<package-name>/` identifies the package; look up which group lists that package. |
| 35 | +- The legacy packages (`roosterjs-editor-*`, except `roosterjs-editor-adapter`) are **not** in `versions.json` and are not versioned by this skill. If a commit only touches legacy packages or non-package files (e.g. `tools/`, `demo/`, root configs), see Step 4. |
| 36 | +- Always re-read the group→package mapping from `tools/buildTools/common.js` in case packages were added or moved. |
| 37 | + |
| 38 | +## Steps |
| 39 | + |
| 40 | +### Step 1: Confirm the commits |
| 41 | + |
| 42 | +Confirm the commits the user provided (see **Inputs**). If none were provided, stop and ask. Resolve PR numbers to commit hashes if needed. Verify each commit exists: |
| 43 | + |
| 44 | +```bash |
| 45 | +git cat-file -t <commit> |
| 46 | +``` |
| 47 | + |
| 48 | +If any commit is invalid/unknown, stop and report it. |
| 49 | + |
| 50 | +### Step 2: Check for uncommitted changes |
| 51 | + |
| 52 | +Only **tracked** modifications (staged or unstaged edits to files git knows about) interfere with switching branches and cherry-picking. **Untracked** files (e.g. a new, not-yet-added skill folder or scratch files) do **not** interfere — do not block on them. |
| 53 | + |
| 54 | +Check for blocking (tracked) changes only: |
| 55 | + |
| 56 | +```bash |
| 57 | +git status --porcelain --untracked-files=no |
| 58 | +``` |
| 59 | + |
| 60 | +- If this produces output, **stop immediately** and ask the user to deal with their tracked uncommitted changes first. |
| 61 | +- If it is empty but `git status --porcelain` (with untracked) shows untracked files, mention them in one line and **continue** — they will be carried along untouched and excluded from the bump commit (which stages only `versions.json`). Note that `gh pr create` may print a harmless "uncommitted change" warning because of them. |
| 62 | + |
| 63 | +### Step 3: Switch to release and pull latest |
| 64 | + |
| 65 | +The hotfix branch is based on `release` (that is what ships). |
| 66 | + |
| 67 | +```bash |
| 68 | +git checkout release |
| 69 | +git pull origin release |
| 70 | +``` |
| 71 | + |
| 72 | +If either command fails, stop and report the error. |
| 73 | + |
| 74 | +**Then check whether each provided commit is already on `release`** — this is the common hotfix situation: the fix lives on `master` and must be cherry-picked onto `release`. Do **not** assume the commit is already there. |
| 75 | + |
| 76 | +```bash |
| 77 | +git merge-base --is-ancestor <commit> release && echo "on release" || echo "NOT on release" |
| 78 | +``` |
| 79 | + |
| 80 | +- If a commit is **already on release**, it does not need cherry-picking (it will be released from `release` directly). |
| 81 | +- If a commit is **not on release**, it must be cherry-picked onto the hotfix branch in Step 5b. Note which commits need cherry-picking and confirm the plan with the user before proceeding (a single `AskUserQuestion` covering cherry-pick + bump level works well). |
| 82 | + |
| 83 | +### Step 4: Determine the files changed by the provided commits |
| 84 | + |
| 85 | +For each provided commit, list the files it changed. Combine across all commits (deduplicate): |
| 86 | + |
| 87 | +```bash |
| 88 | +git show --name-only --pretty=format: <commit> |
| 89 | +``` |
| 90 | + |
| 91 | +For a range, use: |
| 92 | + |
| 93 | +```bash |
| 94 | +git diff --name-only <from>..<to> |
| 95 | +``` |
| 96 | + |
| 97 | +Collect the full set of changed file paths. |
| 98 | + |
| 99 | +**Map files to groups:** |
| 100 | + |
| 101 | +1. For each changed path under `packages/<package-name>/...`, record `<package-name>`. |
| 102 | +2. Look up each package's group using the mapping in **Version groups** (re-confirm from `tools/buildTools/common.js`). |
| 103 | +3. The set of affected groups is the union across all changed files. |
| 104 | + |
| 105 | +**Handle edge cases:** |
| 106 | + |
| 107 | +- If **no package files** were changed (only `tools/`, `demo/`, root configs, docs, etc.), stop and tell the user: "The provided commits do not change any versioned package files, so no version bump is needed." List the changed files so they can confirm. |
| 108 | +- If commits only touch **legacy** packages (`roosterjs-editor-*` other than `roosterjs-editor-adapter`), tell the user those packages are not versioned via `versions.json` and ask how they want to proceed. |
| 109 | + |
| 110 | +Show the user a summary before continuing: |
| 111 | + |
| 112 | +``` |
| 113 | +Affected packages and groups: |
| 114 | +- roosterjs-content-model-plugins → main |
| 115 | +- roosterjs-react → react |
| 116 | +Affected groups to bump: main, react |
| 117 | +``` |
| 118 | + |
| 119 | +### Step 5: Create a new branch based on release |
| 120 | + |
| 121 | +Create a new branch from `release`. Use the naming convention `u/<username>/hotfix-<N>`, picking the next free N (check existing branches so you don't collide), or a name the user specifies: |
| 122 | + |
| 123 | +```bash |
| 124 | +git branch --list 'u/<username>/hotfix-*' # see which N are taken |
| 125 | +git checkout -b <branch_name> release |
| 126 | +``` |
| 127 | + |
| 128 | +### Step 5b: Cherry-pick commits not on release |
| 129 | + |
| 130 | +For each commit identified in Step 3 as **not on release** (in the order the user provided), cherry-pick it onto the new branch: |
| 131 | + |
| 132 | +```bash |
| 133 | +git cherry-pick <commit> |
| 134 | +``` |
| 135 | + |
| 136 | +- If a cherry-pick conflicts, **stop**, show the conflicting files, and ask the user how to proceed (do not guess a resolution). Leave the repo clean if abandoning (`git cherry-pick --abort`). |
| 137 | +- Commits already on release are skipped here. |
| 138 | + |
| 139 | +### Step 6: Determine the SemVer bump for each affected group |
| 140 | + |
| 141 | +A hotfix is normally a **patch** bump (bug fixes, no public API changes). Determine the bump per affected group: |
| 142 | + |
| 143 | +- **Patch** (0.0.x → 0.0.x+1): Bug fixes only, no public interface changes — the default for a hotfix. |
| 144 | +- **Minor** (0.x.0 → 0.x+1.0): New backward-compatible exports/APIs added. |
| 145 | +- **Major** (x.0.0 → x+1.0.0): Breaking changes to public interfaces. |
| 146 | + |
| 147 | +To check whether more than a patch is warranted, inspect the commits' changes to each affected package's `lib/index.ts` barrel files and exported types/interfaces: |
| 148 | + |
| 149 | +```bash |
| 150 | +git show <commit> -- 'packages/**/lib/index.ts' |
| 151 | +``` |
| 152 | + |
| 153 | +- New exports added → minor. |
| 154 | +- Changed/removed existing public signatures → major. |
| 155 | +- Implementation-only changes → patch. |
| 156 | + |
| 157 | +**If a minor or major bump appears needed**, show the interface differences to the user and ask whether they want that bump or to keep it a patch. A hotfix is usually intended to stay a patch — confirm before doing anything larger. |
| 158 | + |
| 159 | +Only bump the groups identified in Step 4. **Leave all other groups untouched.** |
| 160 | + |
| 161 | +### Step 7: Update versions.json |
| 162 | + |
| 163 | +Read the current versions from `versions.json` and apply the bump computed in Step 6 to **only** the affected groups. Do not change groups that were not affected. |
| 164 | + |
| 165 | +### Step 8: Build and test |
| 166 | + |
| 167 | +```bash |
| 168 | +yarn build |
| 169 | +yarn test:fast |
| 170 | +``` |
| 171 | + |
| 172 | +If **any errors** occur, show the full error output and **stop the flow**. Do not proceed with a broken build. |
| 173 | + |
| 174 | +### Step 9: Commit the change |
| 175 | + |
| 176 | +```bash |
| 177 | +git add versions.json |
| 178 | +git commit -m "Hotfix version bump <group>: <old_version> → <new_version>" |
| 179 | +``` |
| 180 | + |
| 181 | +List all changed groups/versions in the commit message. |
| 182 | + |
| 183 | +### Step 10: Push the branch |
| 184 | + |
| 185 | +```bash |
| 186 | +git push origin <branch_name> |
| 187 | +``` |
| 188 | + |
| 189 | +### Step 11: Create a draft PR |
| 190 | + |
| 191 | +Create a draft PR targeting the `release` branch: |
| 192 | + |
| 193 | +**Title:** `Hotfix version bump <group>: <old_version> → <new_version>` (list all affected groups) |
| 194 | + |
| 195 | +**Description:** Include: |
| 196 | + |
| 197 | +1. A table of old/new versions for the affected groups only: |
| 198 | + |
| 199 | +```markdown |
| 200 | +| Group | Old Version | New Version | |
| 201 | +| ----- | ----------- | ----------- | |
| 202 | +| main | x.y.z | x.y.z+1 | |
| 203 | +| react | x.y.z | x.y.z+1 | |
| 204 | +``` |
| 205 | + |
| 206 | +2. The commits included in the hotfix and the packages they touched: |
| 207 | + |
| 208 | +```markdown |
| 209 | +## Hotfix commits |
| 210 | + |
| 211 | +- <short_hash> <commit_subject> |
| 212 | + |
| 213 | +## Affected packages |
| 214 | + |
| 215 | +- roosterjs-content-model-plugins → main |
| 216 | +- roosterjs-react → react |
| 217 | +``` |
| 218 | + |
| 219 | +Command: |
| 220 | + |
| 221 | +```bash |
| 222 | +gh pr create --draft --base release --title "<title>" --body "<description>" |
| 223 | +``` |
| 224 | + |
| 225 | +### Step 12: Show the PR link |
| 226 | + |
| 227 | +``` |
| 228 | +✅ Hotfix version bump PR created successfully! |
| 229 | +PR: https://github.com/microsoft/roosterjs/pull/<number> |
| 230 | +Affected groups: <list> |
| 231 | +``` |
| 232 | + |
| 233 | +## Error Handling |
| 234 | + |
| 235 | +- If no commits are provided, stop and ask the user which commits to hotfix. |
| 236 | +- If any provided commit is invalid, stop and report it. |
| 237 | +- If the commits change no versioned package files, stop — no bump needed. |
| 238 | +- If any git operation fails, show the error and stop. |
| 239 | +- If build/test fails, show errors and stop. |
| 240 | +- If a minor/major bump appears needed, confirm with the user before applying (hotfixes default to patch). |
| 241 | +- Only ever bump groups affected by the provided commits; never touch unaffected groups. |
| 242 | +- Always leave the repo in a clean state if stopping early. |
0 commit comments