Skip to content

Commit 1b57c01

Browse files
authored
feat(cursor-skill): add patch-backstage cursor skill (#4513)
Signed-off-by: Jessica He <jhe@redhat.com>
1 parent 889df1d commit 1b57c01

9 files changed

Lines changed: 915 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
name: patch-backstage
3+
description: >-
4+
Workflow to backport Backstage changes into RHDH by syncing a downstream
5+
maintenance branch and generating yarn patches.
6+
---
7+
# RHDH Patch Generator
8+
9+
## Purpose
10+
11+
Ship a fix on an RHDH **`release-*`** line **without** bumping published Backstage versions by adding **Yarn patches** (`.yarn/patches/`, `package.json` **`resolutions`**, lockfiles).
12+
13+
- **`COMMITS`:** SHAs on **[backstage/backstage](https://github.com/backstage/backstage)** that are on **`master`** (merged fixes). Fetch **`master`** from **`BACKSTAGE_UPSTREAM_REMOTE`** so those objects exist in the **maintenance** clone for **`git show`** / cherry-pick.
14+
- **Build source:** **redhat-developer/backstage** at **`patch/release-<VERSION>`** only (not a separate upstream checkout).
15+
- **Cherry-pick** those SHAs onto maintenance **only** if the **pre-check** shows maintenance source still differs; otherwise **build + patch RHDH** only.
16+
17+
## Repos
18+
19+
| Repo | Role |
20+
|------|------|
21+
| **redhat-developer/rhdh** | Patches live here. Sync **`release-<VERSION>`**; run **`yarn patch`** here (root and/or **`dynamic-plugins/`**). |
22+
| **redhat-developer/backstage** | Maintenance fork: **`patch/release-<VERSION>`**, optional cherry-pick, **`yarn build`** per package **`cd`**, copy **`dist/`** into RHDH patch temps. PRs from your fork. |
23+
| **backstage/backstage** | Upstream object source only: remote on the **same** maintenance clone, **`git fetch`** for **`COMMITS`**. **Do not** use a second upstream checkout as the build tree. |
24+
25+
## Parameters
26+
27+
| Name | Required | Notes |
28+
|------|----------|--------|
29+
| **`RHDH_VERSION`** | Yes | e.g. `1.9`**`release-1.9`**, **`patch/release-1.9`**. Do not infer from the current branch. |
30+
| **`RHDH_ROOT`** | No | Absolute path to the RHDH repo root (appears as **`<RHDH_ROOT>`** in examples). Inferred from context if omitted. |
31+
| **`COMMITS`** | Typical | Upstream SHAs (oldest first for cherry-pick). If no SHAs, manual **`dist`** / patch only. |
32+
| **`PACKAGES`** | If unclear | `@backstage/...` names. Derive from **`COMMITS`** (below) when paths map cleanly. |
33+
34+
**Path map:** `@backstage/plugin-<id>`**`plugins/<id>/`**; other **`@backstage/<id>`****`packages/<id>/`**.
35+
36+
### Deriving **`PACKAGES`** from **`COMMITS`**
37+
38+
In the **maintenance** clone, after **`git fetch <BACKSTAGE_UPSTREAM_REMOTE> master`** so **`COMMITS`** exist locally: **`git show --name-only --pretty=format: <SHA>`** (union for multiple SHAs). Map **`plugins/*`** and **`packages/*`** roots; read each **`package.json`** **`name`**; dedupe. Ignore-only changes (root lockfile, **`.changeset/`**, **`docs/`**, version-only **`package.json`**) → ask which packages to patch.
39+
40+
**Example:** `66e08b08f94a31cbf28b416c89b61549bc3b64a2`**`@backstage/cli-common`**, **`@backstage/backend-plugin-api`**, **`@backstage/plugin-techdocs-node`**.
41+
42+
## Git remotes, hooks, and where this skill file lives
43+
44+
**Map remotes by URL** in **each** clone (`git remote -v`); never assume **`upstream`** means a given org.
45+
46+
- **RHDH core** (`release-*`): URL **redhat-developer/rhdh****`RHDH_CORE_REMOTE`**.
47+
- **Maintenance Backstage** (`patch/release-*`, fork push): URL **redhat-developer/backstage** + usually your fork as **`origin`**.
48+
- **Upstream Backstage** (fetch **`COMMITS`** only): URL **backstage/backstage****`BACKSTAGE_UPSTREAM_REMOTE`**.
49+
50+
Exact maintenance tip:
51+
`git fetch https://github.com/redhat-developer/backstage.git patch/release-<RHDH_VERSION>`
52+
then **`HUSKY=0 git checkout -B patch/release-<RHDH_VERSION> FETCH_HEAD`** when Husky would otherwise run on checkout.
53+
54+
**Silencing hooks:** For branch sync only (Steps 1–2: fetch/checkout/pull/**`checkout -B`**, and **`git cherry-pick`** when you are not relying on hook side effects), prefix with **`HUSKY=0`**. Omit **`HUSKY=0`** on **`git commit`** if you want **lint-staged** locally.
55+
56+
```bash
57+
cd <RHDH_ROOT> && HUSKY=0 git fetch <RHDH_CORE_REMOTE> release-<V> && HUSKY=0 git checkout release-<V> && HUSKY=0 git pull <RHDH_CORE_REMOTE> release-<V>
58+
```
59+
60+
**Rulesync:** Edit **`SKILL.md` only** under **`.rulesync/skills/patch-backstage/`** (rulesync expects **one directory per skill** with **`SKILL.md`** inside; a flat **`*.md`** at **`skills/`** root is ignored). With **`"skills"`** and **`"simulateSkills": true`** in **`rulesync.jsonc`**, **`yarn rulesync:generate`** writes **both** **`.claude/skills/`** and **`.cursor/skills/`** from that tree (rulesync treats Cursor skill output as “simulated”). Stage and commit generated paths with **`.rulesync/`** after edits.
61+
62+
## Agent execution
63+
64+
- **Batch** related shell commands with **`&&`** and **`cd <RHDH_ROOT>`**; cwd may not persist between tool calls. Use **`network` / `git_write` / `all`** as needed (**`all`** for **`rm`/`cp`** into Yarn patch temps or stubborn sandboxes).
65+
- **Stop and ask** when **`RHDH_VERSION`**, **`COMMITS`**, clone paths, or workspace ownership is missing or ambiguous—not for a second confirmation when the user already asked for **yarn patches** for given SHAs (see **Pre-check → patch-only**).
66+
- **Do not** invent remotes or wander with speculative **`find`**; **do** run steps this doc names (**`git remote -v`**, **`yarn why`**, etc.).
67+
68+
## Dist baseline
69+
70+
- **`yarn patch`** overlays **`dist/`** on the **version RHDH already resolves** (lockfile), so **`PACKAGE_VERSION`** must come from **`yarn why`**, not from “what Backstage released.”
71+
- **Compile only** on **redhat-developer/backstage** **`patch/release-<RHDH_VERSION>`** after fetching that ref from **redhat-developer** (local/fork tips can diverge by name).
72+
- **Do not** build from **backstage/backstage** release tags or other upstream checkouts to “match” versions unless this workflow is explicitly extended.
73+
74+
## Workflow (overview)
75+
76+
1. **Pre-flight:** Clean trees and remotes (**Step 1** opening + **Git remotes**); set **`RHDH_CORE_REMOTE`**, **`BACKSTAGE_UPSTREAM_REMOTE`**, optional **`FORK_REMOTE`** (your Backstage fork for PRs).
77+
2. **RHDH:** **`HUSKY=0`** fetch/checkout/pull **`release-<RHDH_VERSION>`**.
78+
3. **Maintenance:** Fetch **`patch/release-*`** from redhat-developer; **`HUSKY=0 checkout -B`**; **`git fetch <BACKSTAGE_UPSTREAM_REMOTE> master`** (upstream integration branch for **`COMMITS`**); **pre-check**; cherry-pick **or** patch-only path; **`yarn build`** per **`PACKAGES`** (**`cd` + `yarn build`**, not root **`yarn workspace … build`**).
79+
4. **RHDH:** Remove stale **`.patch`** + **`resolutions`** for targets; **`yarn why`** → versions; **`yarn patch`** / replace **`dist`** / **`patch-commit`**; **clean up `resolutions`**; **`yarn install`** (root and **`dynamic-plugins/`** as needed).
80+
5. **Verify:** **`@patch:`** in each relevant **`yarn.lock`**; **`yarn why`** shows **`via patch:`**; commit artifacts.
81+
82+
---
83+
84+
## Step 1: Sync RHDH
85+
86+
**Pre-flight (both repos):** **`git status`** clean in **RHDH** and the **maintenance** Backstage clone (stash WIP or **`git merge --abort`** / **`git rebase --abort`** as needed). Do not run the workflow mid-conflict.
87+
88+
1. **`git remote -v`****`RHDH_CORE_REMOTE`** = remote for **redhat-developer/rhdh**.
89+
2. **`HUSKY=0 git fetch … release-<RHDH_VERSION>`** && **`HUSKY=0 git checkout …`** && **`HUSKY=0 git pull …`**. Fail if the branch is missing.
90+
3. Set **`MAINTENANCE_BRANCH`** = **`patch/release-<RHDH_VERSION>`** (used when opening a Backstage PR).
91+
92+
## Step 2: Maintenance clone
93+
94+
**One** clone with **redhat-developer/backstage** + **backstage/backstage** remotes.
95+
96+
### 2.1 Sync `patch/release-*`
97+
98+
```bash
99+
git fetch https://github.com/redhat-developer/backstage.git patch/release-<RHDH_VERSION> \
100+
&& HUSKY=0 git checkout -B patch/release-<RHDH_VERSION> FETCH_HEAD
101+
```
102+
103+
Then **`git fetch <BACKSTAGE_UPSTREAM_REMOTE> master`**. **backstage/backstage** lands merged work on **`master`**; **`COMMITS`** should be reachable from **`master`**. **Do not** check out upstream as the build tree.
104+
105+
### 2.2 Pre-check (skip cherry-pick when source already matches)
106+
107+
For each SHA in **`COMMITS`** (oldest first):
108+
109+
1. **`git show --name-only --pretty=format: <SHA>`**
110+
2. **Drop** bookkeeping-only paths: root **`package.json`**, **`CHANGELOG.md`**, package **`package.json`** version-only edits, **`.changeset/`**, **`docs/`**, lockfiles, etc. **Keep** **`src/`**, tests, fixtures tied to the fix.
111+
3. **`git diff <SHA> HEAD -- <kept paths>`** (union paths if multiple SHAs).
112+
113+
**Empty diff** → maintenance already has the functional fix; **do not cherry-pick** (avoids changelog/version noise).
114+
115+
### 2.3 Patch-only vs ask
116+
117+
- **Proceed** without extra confirmation if the user already asked for **yarn patches** for **`COMMITS`** on this **`release-*`**: empty pre-check → go to **2.6 Build** and RHDH Steps 3–5; note in the **Final summary** that cherry-pick was skipped.
118+
- **Ask** if intent is vague (“sync Backstage” only) or they may want a **maintenance PR** for traceability despite identical source.
119+
120+
### 2.4 Cherry-pick (when pre-check was non-empty)
121+
122+
**`git cherry-pick <SHA>`** (oldest first) onto **`patch/release-*`**.
123+
124+
**Conflicts:** Prefer the **cherry-picked commit’s** content for conflicted **`src/`** (during cherry-pick, **`git checkout --theirs -- <path>`** refers to that commit). If fixing conflicts would drop the functional fix, or conflicts are only **changelog/version** noise you should not merge, **`git cherry-pick --abort`**, note it in the **Final summary**, and coordinate with the user. Do not push a broken maintenance branch.
125+
126+
### 2.5 Push maintenance (optional)
127+
128+
Push to **`FORK_REMOTE`** and open a PR to **redhat-developer/backstage** base **`MAINTENANCE_BRANCH`** (no direct push to **redhat-developer**).
129+
130+
### 2.6 Build **`PACKAGES`**
131+
132+
For each package: **`cd`** **`plugins/<id>/`** or **`packages/<id>/`****`yarn build`**. If **`dist-types`** or build fails: maintenance repo root **`yarn install`** / **`yarn tsc`**, then retry per-package **`yarn build`**.
133+
134+
## Step 3: Prepare RHDH
135+
136+
**`dynamic-plugins/`** is a **separate** Yarn project: its **`yarn.lock`** and **`resolutions`** are independent of the root.
137+
138+
1. Remove prior **`.patch`** files and matching **`resolutions`** entries for the packages you are refreshing (search **both** root and **`dynamic-plugins/package.json`**).
139+
2. **`yarn why @backstage/<pkg>`** from **RHDH root**; if empty, run from **`dynamic-plugins/`**. Record **PACKAGE_VERSION** for Step 4.
140+
141+
## Step 4: Generate patches
142+
143+
Run from **RHDH root** or **`dynamic-plugins/`** depending on where the dependency resolves.
144+
145+
**Temp folder:** Each **`yarn patch …`** prints a **new** path—use it immediately for **`rm`**, **`cp`**, **`yarn patch-commit -s`** (same shell or paste path). Do not reuse an old temp. Sandboxes may need **`all`** for **`cp`** into system temp.
146+
147+
**Per package:**
148+
149+
1. **`yarn patch <package>@npm:<PACKAGE_VERSION>`**
150+
2. **`rm -rf <PATCH_TEMP>/dist`** && **`cp -r <backstage-clone>/<plugins|packages>/.../dist <PATCH_TEMP>/dist`**
151+
3. **`yarn patch-commit -s <PATCH_TEMP>`** in that workspace.
152+
153+
If a package resolves in **both** trees at the **same** version, patch **both** places (or mirror the **`patch:`** resolution into **`dynamic-plugins/package.json`** and run **`yarn install`** there too).
154+
155+
### After **`yarn patch-commit`** (cleanup — required)
156+
157+
1. **Replace** any bare **`"@backstage/foo": "1.2.3"`** in **`resolutions`** with the **single** **`patch:`** locator Yarn printed—**do not** leave bare semver beside new patch keys (patch may not apply; Step 5 will show plain **`@npm:`**).
158+
2. **Delete** spurious range keys **`patch-commit`** added (e.g. **`@backstage/foo@^1.6.0`** → patch built from **`@npm:1.5.0`**), which mis-resolve other workspaces (**`dynamic-plugins/`** may need **`@backstage/backend-plugin-api@1.6.0`** untouched by root).
159+
3. **Install:** **`yarn install`** at root and/or **`dynamic-plugins/`**;
160+
161+
## Step 5: Verify (required)
162+
163+
Incomplete until every patched package shows a **patch locator** in **`yarn.lock`** and **`yarn why`**.
164+
165+
1. **`yarn install`** in each touched project.
166+
2. **`grep '@backstage/<pkg>@patch' yarn.lock`** (run inside **`dynamic-plugins/`** for packages resolved only there).
167+
3. **`yarn why @backstage/<pkg>`** in the same directory: must include **`via patch:`** / **`@…@patch:`**, not only **`via npm:<version>`**.
168+
169+
**Optional:** Spot-check **`node_modules/@backstage/<pkg>/dist/`**.
170+
171+
**Commit:** **`.yarn/patches/*.patch`**, **`package.json`** **`resolutions`**, **`yarn.lock`** (root and/or **`dynamic-plugins/`**); PR to RHDH.
172+
173+
### Final summary
174+
175+
1. Links: `https://github.com/backstage/backstage/commit/<SHA>` for each **`COMMITS`** entry.
176+
2. **`PACKAGES`**, patch locations (root vs **`dynamic-plugins/`**), notable **`resolutions`** keys.
177+
3. Cherry-pick skipped vs applied; conflicts/aborts if any.
178+
4. **`MAINTENANCE_BRANCH`**, optional Backstage PR link; RHDH PR intent.
179+
5. **`RHDH_VERSION`**.
180+
181+
## Safety
182+
183+
- Do not invent **`patch/release-*`** if missing on **redhat-developer/backstage**.
184+
- No blind conflict resolution on cherry-picks; no direct push to **redhat-developer** remotes without process.
185+
186+
## Common issues (quick reference)
187+
188+
| Symptom | What to do |
189+
|---------|------------|
190+
| Wrong maintenance tip | Fetch **`https://github.com/redhat-developer/backstage.git patch/release-<V>`**, **`checkout -B … FETCH_HEAD`** |
191+
| **`bad object`** on cherry-pick / show | **`git fetch <BACKSTAGE_UPSTREAM_REMOTE> master`** |
192+
| Checkout/pull fails after Yarn / Husky | **`HUSKY=0`** on those git commands |
193+
| **`yarn why` / lockfile: no `@patch:`** | Remove bare semver **`resolutions`** for that pkg; use one **`patch:`** locator; drop wrong range keys; reinstall |
194+
| Build fails (missing types) | Maintenance root **`yarn install`** / **`yarn tsc`**, then **`cd`** package **`yarn build`** |
195+
| Patch wrong version | Use **`yarn why`** version, not RHDH meta-version |
196+
| Skill not in Cursor | **`.rulesync/skills/`** may not sync to **`.cursor/`**—see **Rulesync / Cursor** |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# patch-backstage workflow (Mermaid)
2+
3+
Companion diagram for [`SKILL.md`](./SKILL.md). Render in GitHub, GitLab, VS Code (Mermaid preview), or docs tooling.
4+
5+
## Overview
6+
7+
One flow: **maintenance fork** produces **`dist/`**; **RHDH** pins that onto the **lockfile version** with **`yarn patch`**. Upstream **backstage/backstage** is only needed to **fetch** commit objects for cherry-pick / `git show` (optional).
8+
9+
```mermaid
10+
flowchart TB
11+
subgraph repos["Repos"]
12+
direction LR
13+
U(["backstage/backstage<br/><i>optional: source of commits to cherry-pick if provided</i>"])
14+
M(["redhat-developer/backstage<br/>patch/release-V<br/><i>do manual fixes/cherry-picks onto and run yarn build to generate dist/</i>"])
15+
R(["redhat-developer/rhdh<br/>release-V<br/><i>run yarn why/patch/patch-commit, update lockfile, verify patch resolutions</i>"])
16+
end
17+
18+
subgraph work["Workflow"]
19+
direction TB
20+
P[Pre-flight: clean trees, map git remotes] --> S1[Sync RHDH core repo → release-V branch]
21+
S1 --> S2[Sync maintenance repo → patch/release-V branch]
22+
S2 --> S3{Bring fix onto maintenance repo}
23+
S3 -->|Provided upstream SHAs| CP[cherry-pick or skip if changes already present in branch]
24+
S3 -->|manual edits| ME[direct changes in code]
25+
CP --> B[yarn build per package/plugin to generate dist/]
26+
ME --> B
27+
B --> RHDH[yarn why → yarn patch @npm:VERSION → copy dist → patch-commit]
28+
RHDH --> I[yarn install]
29+
I --> V[Verify yarn.lock + yarn why to confirm patch application]
30+
end
31+
```
32+
33+
Step-by-step detail, troubleshooting, and **dynamic-plugins** (second Yarn project) are in [`SKILL.md`](./SKILL.md).

0 commit comments

Comments
 (0)