Skip to content
Merged
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
173 changes: 173 additions & 0 deletions .claude/skills/backstage-upgrade/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
name: backstage-upgrade
description: Upgrade Backstage to the latest stable or specific version. Use when user says "upgrade backstage", "update backstage", "bump backstage", "backstage versions", or when the backstage.json version is behind the latest stable release.
---

# Backstage Upgrade Skill

Use this skill when upgrading a Backstage monorepo to a newer version.

## When to activate this skill

- User asks to upgrade, update, or bump Backstage
- `backstage.json` version is behind latest stable
- User asks what version of Backstage is installed or available
- User asks how to upgrade Backstage

## Prerequisites

### Environment

- Yarn 4.1.1+ (check: `yarn --version`)
- Node.js 20+ or 22+ (check: `node --version`)
- Access to run `mise run` commands

### Recommended

- Backstage Yarn plugin installed (makes version management easier)
- Clean git state (commit or stash changes before upgrading)

## Upgrade Workflow

### Step 1: Check current state

```bash
# Check installed version
cat backstage.json

# Check latest stable version
yarn dlx @backstage/cli@latest --version 2>/dev/null || npx @backstage/cli@latest --version

# Check outdated packages
yarn outdated
```

### Step 2: Install the Backstage Yarn plugin (recommended)

The plugin auto-manages `@backstage/*` versions based on `backstage.json`:

```bash
yarn plugin import https://versions.backstage.io/v1/tags/main/yarn-plugin
```

This creates/updates `.yarnrc.yml` and `.yarn/`. Commit these changes.

### Step 3: Run versions bump

```bash
# Upgrade to latest stable
yarn backstage-cli versions:bump

# Or to a specific version
yarn backstage-cli versions:bump --release 1.51.0

# Or to the 'next' release line (weekly)
yarn backstage-cli versions:bump --release next

# Update custom plugins too (e.g., @roadiehq/*)
yarn backstage-cli versions:bump --pattern '@{backstage,roadiehq}/*'
```

This updates `backstage.json` and migrates package.json deps to use `backstage:^` if the yarn plugin is installed.

### Step 4: Pull in template changes

The `create-app` template changes are NOT auto-applied. Check manually:

```bash
# View changelog
yarn dlx @backstage/create-app@latest --version 2>/dev/null

# Use upgrade helper for diff between versions
open https://backstage.github.io/upgrade-helper/?yarnPlugin=0
```

Key files that often change in templates:
- `packages/backend/src/index.ts`
- `packages/app/src/App.tsx`
- `packages/app/src/components/Root/Root.tsx`
- `app-config.yaml`
- `Dockerfile`

### Step 5: Install updated dependencies

```bash
mise run install
# Or: yarn install
```

### Step 6: Build and verify

```bash
mise run build:all
mise run lint
mise run test
```

### Step 7: Test locally

```bash
mise run dev
```

### Step 8: Commit

```bash
git add -A
git commit -m "chore: upgrade backstage to $(cat backstage.json)"
```

## Key Files

| File | Purpose |
|------|---------|
| `backstage.json` | Master Backstage version for the yarn plugin |
| `.yarnrc.yml` | Yarn configuration (includes plugin if installed) |
| `.yarn/plugins/` | Yarn plugin bundle |
| `packages/app/` | Frontend React app |
| `packages/backend/` | Node.js backend |
| `plugins/` | Custom plugins (scoped `@internal/`) |

## Troubleshooting

### Package mismatches after upgrade

```bash
# Dedupe packages
yarn dedupe
yarn install
```

### Build failures

```bash
# Clean and rebuild
mise run clean
mise run install
mise run build:all
```

### Lock file conflicts

```bash
rm yarn.lock
mise run install
```

### Proxy issues (corporate networks)

```bash
export HTTP_PROXY=http://proxy.company.com:8080
export HTTPS_PROXY=http://proxy.company.com:8080
export NO_PROXY=localhost,internal.company.com
export NODE_USE_ENV_PROXY=1
export YARN_HTTP_PROXY=${HTTP_PROXY}
export YARN_HTTPS_PROXY=${HTTPS_PROXY}
```

## References

- [Keeping Backstage Updated](https://backstage.io/docs/getting-started/keeping-backstage-updated/)
- [Backstage Upgrade Helper](https://backstage.github.io/upgrade-helper/?yarnPlugin=0)
- [Backstage Releases](https://github.com/backstage/backstage/releases)
- [create-app Changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md)
9 changes: 9 additions & 0 deletions .yarn/plugins/@yarnpkg/plugin-backstage.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* eslint-disable */
//prettier-ignore
module.exports = {
name: "@yarnpkg/plugin-backstage",
factory: function (require) {
"use strict";var plugin=(()=>{var nr=Object.create;var C=Object.defineProperty;var or=Object.getOwnPropertyDescriptor;var sr=Object.getOwnPropertyNames;var ir=Object.getPrototypeOf,ar=Object.prototype.hasOwnProperty;var a=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(r,t)=>(typeof require<"u"?require:r)[t]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var l=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports),cr=(e,r)=>{for(var t in r)C(e,t,{get:r[t],enumerable:!0})},se=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of sr(r))!ar.call(e,o)&&o!==t&&C(e,o,{get:()=>r[o],enumerable:!(n=or(r,o))||n.enumerable});return e};var _=(e,r,t)=>(t=e!=null?nr(ir(e)):{},se(r||!e||!e.__esModule?C(t,"default",{value:e,enumerable:!0}):t,e)),ur=e=>se(C({},"__esModule",{value:!0}),e);var pe=l((et,ue)=>{ue.exports=ce;ce.sync=lr;var ie=a("fs");function fr(e,r){var t=r.pathExt!==void 0?r.pathExt:process.env.PATHEXT;if(!t||(t=t.split(";"),t.indexOf("")!==-1))return!0;for(var n=0;n<t.length;n++){var o=t[n].toLowerCase();if(o&&e.substr(-o.length).toLowerCase()===o)return!0}return!1}function ae(e,r,t){return!e.isSymbolicLink()&&!e.isFile()?!1:fr(r,t)}function ce(e,r,t){ie.stat(e,function(n,o){t(n,n?!1:ae(o,e,r))})}function lr(e,r){return ae(ie.statSync(e),e,r)}});var de=l((rt,me)=>{me.exports=le;le.sync=gr;var fe=a("fs");function le(e,r,t){fe.stat(e,function(n,o){t(n,n?!1:ge(o,r))})}function gr(e,r){return ge(fe.statSync(e),r)}function ge(e,r){return e.isFile()&&mr(e,r)}function mr(e,r){var t=e.mode,n=e.uid,o=e.gid,s=r.uid!==void 0?r.uid:process.getuid&&process.getuid(),i=r.gid!==void 0?r.gid:process.getgid&&process.getgid(),c=parseInt("100",8),f=parseInt("010",8),u=parseInt("001",8),d=c|f,g=t&u||t&f&&o===i||t&c&&n===s||t&d&&s===0;return g}});var we=l((nt,he)=>{var tt=a("fs"),A;process.platform==="win32"||global.TESTING_WINDOWS?A=pe():A=de();he.exports=F;F.sync=dr;function F(e,r,t){if(typeof r=="function"&&(t=r,r={}),!t){if(typeof Promise!="function")throw new TypeError("callback not provided");return new Promise(function(n,o){F(e,r||{},function(s,i){s?o(s):n(i)})})}A(e,r||{},function(n,o){n&&(n.code==="EACCES"||r&&r.ignoreErrors)&&(n=null,o=!1),t(n,o)})}function dr(e,r){try{return A.sync(e,r||{})}catch(t){if(r&&r.ignoreErrors||t.code==="EACCES")return!1;throw t}}});var Re=l((ot,Pe)=>{var x=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",Ee=a("path"),hr=x?";":":",ye=we(),ve=e=>Object.assign(new Error(`not found: ${e}`),{code:"ENOENT"}),xe=(e,r)=>{let t=r.colon||hr,n=e.match(/\//)||x&&e.match(/\\/)?[""]:[...x?[process.cwd()]:[],...(r.path||process.env.PATH||"").split(t)],o=x?r.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",s=x?o.split(t):[""];return x&&e.indexOf(".")!==-1&&s[0]!==""&&s.unshift(""),{pathEnv:n,pathExt:s,pathExtExe:o}},ke=(e,r,t)=>{typeof r=="function"&&(t=r,r={}),r||(r={});let{pathEnv:n,pathExt:o,pathExtExe:s}=xe(e,r),i=[],c=u=>new Promise((d,g)=>{if(u===n.length)return r.all&&i.length?d(i):g(ve(e));let m=n[u],P=/^".*"$/.test(m)?m.slice(1,-1):m,y=Ee.join(P,e),L=!P&&/^\.[\\\/]/.test(e)?e.slice(0,2)+y:y;d(f(L,u,0))}),f=(u,d,g)=>new Promise((m,P)=>{if(g===o.length)return m(c(d+1));let y=o[g];ye(u+y,{pathExt:s},(L,tr)=>{if(!L&&tr)if(r.all)i.push(u+y);else return m(u+y);return m(f(u,d,g+1))})});return t?c(0).then(u=>t(null,u),t):c(0)},wr=(e,r)=>{r=r||{};let{pathEnv:t,pathExt:n,pathExtExe:o}=xe(e,r),s=[];for(let i=0;i<t.length;i++){let c=t[i],f=/^".*"$/.test(c)?c.slice(1,-1):c,u=Ee.join(f,e),d=!f&&/^\.[\\\/]/.test(e)?e.slice(0,2)+u:u;for(let g=0;g<n.length;g++){let m=d+n[g];try{if(ye.sync(m,{pathExt:o}))if(r.all)s.push(m);else return m}catch{}}}if(r.all&&s.length)return s;if(r.nothrow)return null;throw ve(e)};Pe.exports=ke;ke.sync=wr});var Se=l((st,I)=>{"use strict";var Oe=(e={})=>{let r=e.env||process.env;return(e.platform||process.platform)!=="win32"?"PATH":Object.keys(r).reverse().find(n=>n.toUpperCase()==="PATH")||"Path"};I.exports=Oe;I.exports.default=Oe});var $e=l((it,Ce)=>{"use strict";var be=a("path"),Er=Re(),yr=Se();function Te(e,r){let t=e.options.env||process.env,n=process.cwd(),o=e.options.cwd!=null,s=o&&process.chdir!==void 0&&!process.chdir.disabled;if(s)try{process.chdir(e.options.cwd)}catch{}let i;try{i=Er.sync(e.command,{path:t[yr({env:t})],pathExt:r?be.delimiter:void 0})}catch{}finally{s&&process.chdir(n)}return i&&(i=be.resolve(o?e.options.cwd:"",i)),i}function vr(e){return Te(e)||Te(e,!0)}Ce.exports=vr});var Ae=l((at,M)=>{"use strict";var W=/([()\][%!^"`<>&|;, *?])/g;function xr(e){return e=e.replace(W,"^$1"),e}function kr(e,r){return e=`${e}`,e=e.replace(/(?=(\\+?)?)\1"/g,'$1$1\\"'),e=e.replace(/(?=(\\+?)?)\1$/,"$1$1"),e=`"${e}"`,e=e.replace(W,"^$1"),r&&(e=e.replace(W,"^$1")),e}M.exports.command=xr;M.exports.argument=kr});var Ne=l((ct,De)=>{"use strict";De.exports=/^#!(.*)/});var Le=l((ut,Be)=>{"use strict";var Pr=Ne();Be.exports=(e="")=>{let r=e.match(Pr);if(!r)return null;let[t,n]=r[0].replace(/#! ?/,"").split(" "),o=t.split("/").pop();return o==="env"?n:n?`${o} ${n}`:o}});var Ue=l((pt,_e)=>{"use strict";var H=a("fs"),Rr=Le();function Or(e){let t=Buffer.alloc(150),n;try{n=H.openSync(e,"r"),H.readSync(n,t,0,150,0),H.closeSync(n)}catch{}return Rr(t.toString())}_e.exports=Or});var Ie=l((ft,Fe)=>{"use strict";var Sr=a("path"),je=$e(),Ve=Ae(),br=Ue(),Tr=process.platform==="win32",Cr=/\.(?:com|exe)$/i,$r=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function Ar(e){e.file=je(e);let r=e.file&&br(e.file);return r?(e.args.unshift(e.file),e.command=r,je(e)):e.file}function Dr(e){if(!Tr)return e;let r=Ar(e),t=!Cr.test(r);if(e.options.forceShell||t){let n=$r.test(r);e.command=Sr.normalize(e.command),e.command=Ve.command(e.command),e.args=e.args.map(s=>Ve.argument(s,n));let o=[e.command].concat(e.args).join(" ");e.args=["/d","/s","/c",`"${o}"`],e.command=process.env.comspec||"cmd.exe",e.options.windowsVerbatimArguments=!0}return e}function Nr(e,r,t){r&&!Array.isArray(r)&&(t=r,r=null),r=r?r.slice(0):[],t=Object.assign({},t);let n={command:e,args:r,options:t,file:void 0,original:{command:e,args:r}};return t.shell?n:Dr(n)}Fe.exports=Nr});var He=l((lt,Me)=>{"use strict";var G=process.platform==="win32";function q(e,r){return Object.assign(new Error(`${r} ${e.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${r} ${e.command}`,path:e.command,spawnargs:e.args})}function Br(e,r){if(!G)return;let t=e.emit;e.emit=function(n,o){if(n==="exit"){let s=We(o,r);if(s)return t.call(e,"error",s)}return t.apply(e,arguments)}}function We(e,r){return G&&e===1&&!r.file?q(r.original,"spawn"):null}function Lr(e,r){return G&&e===1&&!r.file?q(r.original,"spawnSync"):null}Me.exports={hookChildProcess:Br,verifyENOENT:We,verifyENOENTSync:Lr,notFoundError:q}});var Xe=l((gt,k)=>{"use strict";var Ge=a("child_process"),X=Ie(),Y=He();function qe(e,r,t){let n=X(e,r,t),o=Ge.spawn(n.command,n.args,n.options);return Y.hookChildProcess(o,n),o}function _r(e,r,t){let n=X(e,r,t),o=Ge.spawnSync(n.command,n.args,n.options);return o.error=o.error||Y.verifyENOENTSync(o.status,n),o}k.exports=qe;k.exports.spawn=qe;k.exports.sync=_r;k.exports._parse=X;k.exports._enoent=Y});var Jr={};cr(Jr,{default:()=>Yr});var B=a("@yarnpkg/core");var b=a("@yarnpkg/core");var K=_(a("assert")),Qe=a("semver"),N=a("@yarnpkg/fslib");var $=_(a("fs")),v=a("path");function pr(e,r){let t=e;for(let n=0;n<1e3;n++){let o=(0,v.resolve)(t,"package.json");if($.default.existsSync(o)&&r(o))return t;let i=(0,v.dirname)(t);if(i===t)return;t=i}throw new Error(`Iteration limit reached when searching for root package.json at ${e}`)}var h;var U=class{#t;#r;#e;get dir(){if(h)return h.dir;let r=process.cwd();return this.#r!==void 0&&this.#t===r?this.#r:(this.#t=r,this.#e=void 0,this.#r=$.default.realpathSync(r).replace(/^[a-z]:/,t=>t.toLocaleUpperCase("en-US")),this.#r)}get rootDir(){if(h)return h.rootDir;let r=this.dir;return this.#e!==void 0?this.#e:(this.#e=pr(r,t=>{try{let n=$.default.readFileSync(t,"utf8");return!!JSON.parse(n).workspaces}catch(n){throw new Error(`Failed to parse package.json file while searching for root, ${n}`)}})??r,this.#e)}resolve=(...r)=>h?h.resolve(...r):(0,v.resolve)(this.dir,...r);resolveRoot=(...r)=>h?h.resolveRoot(...r):(0,v.resolve)(this.rootDir,...r)},j=new U;var V="backstage.json";var jr=_(Xe());function J(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;let r=e;return!(typeof r.name!="string"||r.name===""||typeof r.message!="string")}function Ye(e){if(J(e))return e;if(typeof e=="string")return new Error(e);try{return new Error(`unknown error '${e}'`)}catch{return new Error(`unknown error of type '${typeof e}'`)}}var D=class extends Error{cause;constructor(r,t){let n=t!==void 0?Ye(t):void 0,o=r;if(n!==void 0){let s=String(n),i=s!=="[object Object]"?s:`${n.name}: ${n.message}`;o?o+=`; caused by ${i}`:o=`caused by ${i}`}super(o),Error.captureStackTrace?.(this,this.constructor),(!this.name||this.name==="Error")&&this.constructor.name!=="Error"&&(this.name=this.constructor.name),this.cause=n}};var R=class extends D{constructor(r,t){super(r,t),this.name=J(t)?t.name:"Error"}};var Je=e=>{let r=!1,t;return()=>(r||(t=e(),r=!0),t)};var Ke=a("@yarnpkg/fslib");var ze=()=>Ke.npath.toPortablePath(j.rootDir);var O=Je(()=>{let e=N.ppath.join(ze(),V),r=null;try{let t=N.xfs.readJsonSync(e).version;(0,K.default)(t!==void 0,"Version field is missing"),r=(0,Qe.valid)(t),(0,K.default)(r!==null,"Version exists but is not valid semver")}catch(t){throw new R("Valid version string not found in backstage.json",t)}return r});var S=a("@yarnpkg/core"),Ze=a("@yarnpkg/fslib");var Vr="https://versions.backstage.io",Fr="https://raw.githubusercontent.com/backstage/versions/main";function Ir(e,r){return new Promise((t,n)=>{let o=setTimeout(()=>{r.aborted||t()},e);r.addEventListener("abort",()=>{clearTimeout(o),n(new Error("Aborted"))})})}async function Wr(e,r,t){let n=new AbortController,o=new AbortController,s=e(n.signal).then(c=>(o.abort(),c)),i=Ir(t,o.signal).then(()=>r(o.signal)).then(c=>(n.abort(),c));return Promise.any([s,i]).catch(()=>s)}async function z(e){let r=encodeURIComponent(e.version),t=e.fetch??fetch,n=e.versionsBaseUrl??Vr,o=e.gitHubRawBaseUrl??Fr,s=await Wr(i=>t(`${n}/v1/releases/${r}/manifest.json`,{signal:i}),i=>t(`${o}/v1/releases/${r}/manifest.json`,{signal:i}),500);if(s.status===404)throw new Error(`No release found for ${e.version} version`);if(s.status!==200)throw new Error(`Unexpected response status ${s.status} when fetching release from ${s.url}.`);return s.json()}var p="backstage:";var Q=a("process"),E=async(e,r)=>{let t=S.structUtils.stringifyIdent(e),n=S.structUtils.parseRange(e.range);if(n.protocol!==p)throw new Error(`Unsupported version protocol in version range "${e.range}" for package ${t}`);if(n.selector!=="^")throw new Error(`Unexpected version selector "${n.selector}" for package ${t}`);let o=O(),s=Q.env.BACKSTAGE_MANIFEST_FILE,c=(s?await Ze.xfs.readJsonSync(s):await z({version:o,versionsBaseUrl:Q.env.BACKSTAGE_VERSIONS_BASE_URL,fetch:async f=>{let u=await S.httpUtils.get(f,{configuration:r,jsonResponse:!0});return{status:200,url:f,json:()=>u}}})).packages.find(f=>f.name===t);if(!c)throw new Error(`Package ${t} not found in manifest for Backstage v${o}. This means the specified package is not included in this Backstage release. This may imply the package has been replaced with an alternative - please review the documentation for the package. If you need to continue using this package, it will be necessary to switch to manually managing its version.`);return c.version};var Mr=e=>b.structUtils.parseRange(e).protocol===p,Hr=(e,r,t)=>e!=="dependencies"?e:t.manifest.ensureDependencyMeta(b.structUtils.makeDescriptor(r,"unknown")).optional?"optionalDependencies":e,Z=async(e,r)=>{for(let t of["dependencies","devDependencies"]){let n=Array.from(e.manifest.getForScope(t).values()).filter(o=>o.range.startsWith(p));for(let o of n){let s=b.structUtils.stringifyIdent(o);if(b.structUtils.parseRange(o.range).selector!=="^")throw new Error(`Unexpected version range "${o.range}" for dependency on "${s}"`);let c=Hr(t,o,e);r[c][s]=`^${await E(o,e.project.configuration)}`}}if(["dependencies","devDependencies","optionalDependencies"].some(t=>Object.values(r[t]??{}).some(Mr)))throw new Error(`Failed to replace all "backstage:" ranges in manifest for ${r.name}`)};var ee=a("@yarnpkg/core");var re=async(e,r)=>{let t=ee.structUtils.parseRange(e.range);if(t.protocol!==p)return e;if(t.selector!=="^")throw new Error(`Invalid backstage: version range found: ${e.range}`);return ee.structUtils.bindDescriptor(e,{backstage:O(),npm:await E(e,r.configuration)})};var er=a("@yarnpkg/core");var te=async(e,r,t,n)=>{let o=er.structUtils.parseRange(t.range);if(t.scope==="backstage"&&o.protocol!==p){let s=t.range;try{t.range=`${p}^`,await E(t,e.project.configuration),console.info(`Setting ${t.scope}/${t.name} to ${p}^`)}catch{t.range=s}}};var rr=a("@yarnpkg/core");var ne=async(e,r,t,n)=>{let o=rr.structUtils.parseRange(n.range);n.scope==="backstage"&&o.protocol!==p&&console.warn(`${n.name} should be set to "${p}^" instead of "${n.range}". Make sure this change is intentional and not a mistake.`)};var w=a("@yarnpkg/core"),oe=a("@yarnpkg/plugin-npm");var T=class e{static protocol=p;supportsDescriptor=r=>r.range.startsWith(e.protocol);async getCandidates(r,t,n){let o=w.structUtils.parseRange(r.range).params?.npm;if(!o||Array.isArray(o))throw new Error(`Missing npm parameter on backstage: range "${r.range}"`);return new oe.NpmSemverResolver().getCandidates(w.structUtils.makeDescriptor(r,`npm:^${o}`),t,n)}getResolutionDependencies(r){let t=w.structUtils.parseRange(r.range).params?.npm;if(!t)throw new Error(`Missing npm parameter on backstage: range "${r.range}".`);return{[w.structUtils.stringifyIdent(r)]:w.structUtils.makeDescriptor(r,`npm:^${t}`)}}async getSatisfying(r,t,n,o){let s=r,i=w.structUtils.parseRange(s.range);if(i.protocol===p){let c=i.params?.npm;s=w.structUtils.makeDescriptor(r,`npm:^${c}`)}return new oe.NpmSemverResolver().getSatisfying(s,t,n,o)}bindDescriptor=r=>r;supportsLocator=()=>!1;shouldPersistResolution=()=>{throw new Error("Unreachable: BackstageNpmResolver should never persist resolution as it uses npm: protocol")};resolve=async()=>{throw new Error("Unreachable: BackstageNpmResolver should never resolve as it uses npm: protocol")}};var Gr="\x1B[31;1m",qr="\x1B[0m";B.semverUtils.satisfiesWithPrereleases(B.YarnVersion,"^4.1.1")||(console.error(),console.error(`${Gr}Unsupported yarn version${qr}: The Backstage yarn plugin only works with yarn ^4.1.1. Please upgrade yarn, or remove this plugin with "yarn plugin remove @yarnpkg/plugin-backstage".`),console.error());var Xr={hooks:{afterWorkspaceDependencyAddition:te,afterWorkspaceDependencyReplacement:ne,reduceDependency:re,beforeWorkspacePacking:Z},resolvers:[T]},Yr=Xr;return ur(Jr);})();
return plugin;
}
};
7 changes: 6 additions & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
nodeLinker: node-modules
nodeLinker: node-modules

plugins:
- checksum: 0cfdc882d3c1395592aa6d4690958e70ebbc3a8ee1d888d523dc9e3c74796de26f744c37df66a69212280634f422a88074ba625dce0f7886fbdf2a904a0c38a2
path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs
spec: "https://versions.backstage.io/v1/releases/1.51.1/yarn-plugin"
2 changes: 1 addition & 1 deletion backstage.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "1.46.0"
"version": "1.51.1"
}
Loading
Loading