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
5 changes: 5 additions & 0 deletions .changeset/great-cooks-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pagerduty/backstage-plugin-backend': patch
---

Remove remainings of old backend system
5 changes: 5 additions & 0 deletions .changeset/heavy-chefs-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pagerduty/backstage-plugin': patch
---

Migrate backstage plugin's EntityPagerDutyCard to Backstage UI
5 changes: 5 additions & 0 deletions .changeset/quick-signs-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pagerduty/backstage-plugin': patch
---

Migrated the PagerDutyCard component to backtage ui
8 changes: 8 additions & 0 deletions .changeset/sad-teeth-show.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@pagerduty/backstage-plugin-scaffolder-actions': patch
'@pagerduty/backstage-plugin-entity-processor': patch
'@pagerduty/backstage-plugin-backend': patch
'@pagerduty/backstage-plugin': patch
---

Fix package.json metadata to improve Portal relations
9 changes: 9 additions & 0 deletions .changeset/slimy-mangos-write.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@pagerduty/backstage-plugin': minor
---

Best practice implementation of the new frontend system

- Better separation of concerns.
- New use of the API for new frontend system.
- Made the @backstage/ui dependency a peer-dependency that needs to be imported and used by Backstage users.
5 changes: 5 additions & 0 deletions .changeset/strong-falcons-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pagerduty/backstage-plugin': patch
---

Change module main entry for backstage-plugin (frontend)
5 changes: 5 additions & 0 deletions .changeset/three-lemons-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pagerduty/backstage-plugin': patch
---

Implement new frontend system (alpha)
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 F=Object.create;var v=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,I=Object.prototype.hasOwnProperty;var p=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var J=(e,t)=>{for(var r in t)v(e,r,{get:t[r],enumerable:!0})},S=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of N(t))!I.call(e,o)&&o!==r&&v(e,o,{get:()=>t[o],enumerable:!(n=_(t,o))||n.enumerable});return e};var L=(e,t,r)=>(r=e!=null?F(G(e)):{},S(t||!e||!e.__esModule?v(r,"default",{value:e,enumerable:!0}):r,e)),z=e=>S(v({},"__esModule",{value:!0}),e);var ae={};J(ae,{default:()=>se});var P=p("@yarnpkg/core");var w=p("@yarnpkg/core");var W=L(p("assert")),j=p("semver"),y=p("@yarnpkg/fslib");var u=L(p("fs")),g=p("path");function A(e,t){let r=e;for(let n=0;n<1e3;n++){let o=(0,g.resolve)(r,"package.json");if(u.default.existsSync(o)&&t(o))return r;let i=(0,g.dirname)(r);if(i===r)return;r=i}throw new Error(`Iteration limit reached when searching for root package.json at ${e}`)}function K(e){let t=A(e,()=>!0);if(!t)throw new Error(`No package.json found while searching for package root of ${e}`);return t}function Y(e){if(!u.default.existsSync((0,g.resolve)(e,"src")))throw new Error("Tried to access monorepo package root dir outside of Backstage repository");return(0,g.resolve)(e,"../..")}function b(e){let t=K(e),r=u.default.realpathSync(process.cwd()).replace(/^[a-z]:/,s=>s.toLocaleUpperCase("en-US")),n="",o=()=>(n||(n=Y(t)),n),a="",i=()=>(a||(a=A(r,s=>{try{let m=u.default.readFileSync(s,"utf8");return!!JSON.parse(m).workspaces}catch(m){throw new Error(`Failed to parse package.json file while searching for root, ${m}`)}})??r),a);return{ownDir:t,get ownRoot(){return o()},targetDir:r,get targetRoot(){return i()},resolveOwn:(...s)=>(0,g.resolve)(t,...s),resolveOwnRoot:(...s)=>(0,g.resolve)(o(),...s),resolveTarget:(...s)=>(0,g.resolve)(r,...s),resolveTargetRoot:(...s)=>(0,g.resolve)(i(),...s)}}var x="backstage.json";var V=e=>{let t=!1,r;return()=>(t||(r=e(),t=!0),r)};var h=p("@yarnpkg/fslib");var C=()=>h.npath.toPortablePath(b(h.npath.fromPortablePath(h.ppath.cwd())).targetRoot);var k=V(()=>{let e=y.ppath.join(C(),x),t=null;try{t=(0,j.valid)(y.xfs.readJsonSync(e).version),(0,W.default)(t!==null)}catch{throw new Error("Valid version string not found in backstage.json")}return t});var d=p("@yarnpkg/core");var q="https://versions.backstage.io",Q="https://raw.githubusercontent.com/backstage/versions/main";function X(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>{t.aborted||r()},e);t.addEventListener("abort",()=>{clearTimeout(o),n(new Error("Aborted"))})})}async function Z(e,t,r){let n=new AbortController,o=new AbortController,a=e(n.signal).then(s=>(o.abort(),s)),i=X(r,o.signal).then(()=>t(o.signal)).then(s=>(n.abort(),s));return Promise.any([a,i]).catch(()=>a)}async function D(e){let t=encodeURIComponent(e.version),r=e.fetch??fetch,n=e.versionsBaseUrl??q,o=e.gitHubRawBaseUrl??Q,a=await Z(i=>r(`${n}/v1/releases/${t}/manifest.json`,{signal:i}),i=>r(`${o}/v1/releases/${t}/manifest.json`,{signal:i}),500);if(a.status===404)throw new Error(`No release found for ${e.version} version`);if(a.status!==200)throw new Error(`Unexpected response status ${a.status} when fetching release from ${a.url}.`);return a.json()}var c="backstage:";var f=async(e,t)=>{let r=d.structUtils.stringifyIdent(e),n=d.structUtils.parseRange(e.range);if(n.protocol!==c)throw new Error(`Unsupported version protocol in version range "${e.range}" for package ${r}`);if(n.selector!=="^")throw new Error(`Unexpected version selector "${n.selector}" for package ${r}`);let o=k(),i=(await D({version:o,fetch:async s=>{let m=await d.httpUtils.get(s,{configuration:t,jsonResponse:!0});return{status:200,url:s,json:()=>m}}})).packages.find(s=>s.name===r);if(!i)throw new Error(`Package ${r} 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 i.version};var ee=e=>w.structUtils.parseRange(e).protocol===c,te=(e,t,r)=>e!=="dependencies"?e:r.manifest.ensureDependencyMeta(w.structUtils.makeDescriptor(t,"unknown")).optional?"optionalDependencies":e,B=async(e,t)=>{for(let r of["dependencies","devDependencies"]){let n=Array.from(e.manifest.getForScope(r).values()).filter(o=>o.range.startsWith(c));for(let o of n){let a=w.structUtils.stringifyIdent(o);if(w.structUtils.parseRange(o.range).selector!=="^")throw new Error(`Unexpected version range "${o.range}" for dependency on "${a}"`);let s=te(r,o,e);t[s][a]=`^${await f(o,e.project.configuration)}`}}if(["dependencies","devDependencies","optionalDependencies"].some(r=>Object.values(t[r]??{}).some(ee)))throw new Error(`Failed to replace all "backstage:" ranges in manifest for ${t.name}`)};var O=p("@yarnpkg/core");var $=async(e,t)=>{let r=O.structUtils.parseRange(e.range);if(r.protocol!==c)return e;if(r.selector!=="^")throw new Error(`Invalid backstage: version range found: ${e.range}`);return O.structUtils.bindDescriptor(e,{backstage:k(),npm:await f(e,t.configuration)})};var H=p("@yarnpkg/core");var U=async(e,t,r,n)=>{let o=H.structUtils.parseRange(r.range);if(r.scope==="backstage"&&o.protocol!==c){let a=r.range;try{r.range=`${c}^`,await f(r,e.project.configuration),console.info(`Setting ${r.scope}/${r.name} to ${c}^`)}catch{r.range=a}}};var M=p("@yarnpkg/core");var E=async(e,t,r,n)=>{let o=M.structUtils.parseRange(n.range);n.scope==="backstage"&&o.protocol!==c&&console.warn(`${n.name} should be set to "${c}^" instead of "${n.range}". Make sure this change is intentional and not a mistake.`)};var l=p("@yarnpkg/core"),T=p("@yarnpkg/plugin-npm");var R=class e{static protocol=c;supportsDescriptor=t=>t.range.startsWith(e.protocol);async getCandidates(t,r,n){let o=l.structUtils.parseRange(t.range).params?.npm;if(!o||Array.isArray(o))throw new Error(`Missing npm parameter on backstage: range "${t.range}"`);return new T.NpmSemverResolver().getCandidates(l.structUtils.makeDescriptor(t,`npm:^${o}`),r,n)}getResolutionDependencies(t){let r=l.structUtils.parseRange(t.range).params?.npm;if(!r)throw new Error(`Missing npm parameter on backstage: range "${t.range}".`);return{[l.structUtils.stringifyIdent(t)]:l.structUtils.makeDescriptor(t,`npm:^${r}`)}}async getSatisfying(t,r,n,o){let a=t,i=l.structUtils.parseRange(a.range);if(i.protocol===c){let s=i.params?.npm;a=l.structUtils.makeDescriptor(t,`npm:^${s}`)}return new T.NpmSemverResolver().getSatisfying(a,r,n,o)}bindDescriptor=t=>t;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 re="\x1B[31;1m",oe="\x1B[0m";P.semverUtils.satisfiesWithPrereleases(P.YarnVersion,"^4.1.1")||(console.error(),console.error(`${re}Unsupported yarn version${oe}: 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 ne={hooks:{afterWorkspaceDependencyAddition:U,afterWorkspaceDependencyReplacement:E,reduceDependency:$,beforeWorkspacePacking:B},resolvers:[R]},se=ne;return z(ae);})();
return plugin;
}
};
16 changes: 14 additions & 2 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
nodeLinker: node-modules

enableGlobalCache: true

nmHoistingLimits: none

nmMode: hardlinks-local

pnpFallbackMode: none

supportedArchitectures:
os: [current]
cpu: [current]
os:
- current
cpu:
- current

npmRegistryServer: 'https://registry.npmjs.org/'

Expand All @@ -14,4 +21,9 @@ npmScopes:
npmAlwaysAuth: true
npmAuthToken: NPM_TOKEN

plugins:
- checksum: 8af7b3f2d7d19cacc7a3712f871efcb6208ba283a1f532260b0cba80c2cb66ed772b207b5ba41b8c5d64dd8d5e0c0e15bbb445bd14afac491712965211ba027c
path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs
spec: "https://versions.backstage.io/v1/releases/1.42.5/yarn-plugin"

yarnPath: .yarn/releases/yarn-4.4.1.cjs
1 change: 1 addition & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ pagerDuty:
clientId: ${PAGERDUTY_CLIENT_ID}
clientSecret: ${PAGERDUTY_CLIENT_SECRET}
subDomain: ${PAGERDUTY_SUBDOMAIN}
region: ${PAGERDUTY_OAUTH_REGION}
36 changes: 19 additions & 17 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/app-defaults": "^1.6.3",
"@backstage/app-defaults": "^1.7.0",
"@backstage/canon": "^0.5.0",
"@backstage/catalog-model": "^1.7.4",
"@backstage/cli": "^0.33.0",
"@backstage/core-app-api": "^1.17.1",
"@backstage/core-components": "^0.17.3",
"@backstage/core-plugin-api": "^1.10.8",
"@backstage/integration-react": "^1.2.8",
"@backstage/plugin-api-docs": "^0.12.8",
"@backstage/plugin-catalog": "^1.31.0",
"@backstage/plugin-catalog-common": "^1.1.4",
"@backstage/plugin-catalog-graph": "^0.4.20",
"@backstage/plugin-catalog-import": "^0.13.1",
"@backstage/plugin-catalog-react": "^1.19.0",
"@backstage/catalog-model": "^1.7.5",
"@backstage/cli": "^0.34.3",
"@backstage/core-app-api": "^1.19.0",
"@backstage/core-components": "^0.18.0",
"@backstage/core-plugin-api": "^1.11.0",
"@backstage/frontend-defaults": "^0.3.1",
"@backstage/integration-react": "^1.2.10",
"@backstage/plugin-api-docs": "^0.12.11",
"@backstage/plugin-catalog": "^1.31.3",
"@backstage/plugin-catalog-common": "^1.1.5",
"@backstage/plugin-catalog-graph": "^0.5.0",
"@backstage/plugin-catalog-import": "^0.13.5",
"@backstage/plugin-catalog-react": "^1.21.0",
"@backstage/plugin-kubernetes": "^0.12.8",
"@backstage/plugin-org": "^0.6.40",
"@backstage/plugin-org": "^0.6.41",
"@backstage/plugin-permission-react": "^0.4.35",
"@backstage/plugin-scaffolder": "^1.32.0",
"@backstage/plugin-search": "^1.4.27",
"@backstage/plugin-scaffolder": "^1.33.0",
"@backstage/plugin-search": "^1.4.28",
"@backstage/plugin-search-react": "^1.9.1",
"@backstage/plugin-techdocs": "^1.13.0",
"@backstage/plugin-techdocs": "^1.13.2",
"@backstage/plugin-techdocs-module-addons-contrib": "^1.1.25",
"@backstage/plugin-techdocs-react": "^1.3.0",
"@backstage/plugin-user-settings": "^0.8.23",
"@backstage/theme": "^0.6.6",
"@backstage/ui": "^0.7.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@pagerduty/backstage-plugin": "workspace:^",
Expand Down
26 changes: 26 additions & 0 deletions packages/app/src/App-Alpha.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ApiExplorerPlugin from '@backstage/plugin-api-docs/alpha';
import ApiDocsPlugin from '@backstage/plugin-api-docs/alpha';
import CatalogPlugin from '@backstage/plugin-catalog/alpha';
import CatalogImportPlugin from '@backstage/plugin-catalog-import/alpha';
import ScaffolderPlugin from '@backstage/plugin-scaffolder/alpha';
import OrgPlugin from '@backstage/plugin-org/alpha';
import SearchPlugin from '@backstage/plugin-search/alpha';
import TechDocsPlugin from '@backstage/plugin-techdocs/alpha';
import { createApp } from '@backstage/frontend-defaults';
import pagerDutyPlugin from '@pagerduty/backstage-plugin/alpha';

const app = createApp({
features: [
ApiExplorerPlugin,
ApiDocsPlugin,
CatalogPlugin,
CatalogImportPlugin,
ScaffolderPlugin,
SearchPlugin,
OrgPlugin,
TechDocsPlugin,
pagerDutyPlugin,
],
});

export default app.createRoot();
14 changes: 12 additions & 2 deletions packages/app/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import '@backstage/cli/asset-types';
import ReactDOM from 'react-dom/client';
import App from './App';
import '@backstage/canon/css/styles.css';
import '@backstage/ui/css/styles.css';

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
// Uncomment the lines below if you want to use the old version of the app
// import App from './App';

// ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

// Uncomment the lines below if you want to use the alpha version of the app
import AppAlpha from './App-Alpha';

ReactDOM.createRoot(document.getElementById('root')!).render(
<div data-theme={localStorage.getItem('theme') ?? 'light'}>{AppAlpha}</div>,
);
17 changes: 0 additions & 17 deletions plugins/backstage-plugin-backend/src/run.ts

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions plugins/backstage-plugin-entity-processor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin-module",
"pluginId": "catalog",
"pluginPackage": "pagerduty-entity-processor"
"role": "backend-plugin-module",
"pluginPackage": "@backstage/plugin-catalog-backend"
},
"homepage": "https://pagerduty.github.io/backstage-plugin-docs/index.html",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions plugins/backstage-plugin-scaffolder-actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"types": "dist/index.d.ts"
},
"backstage": {
"pluginId": "scaffolder",
"role": "backend-plugin-module",
"pluginPackage": "scaffolder",
"pluginId": "pagerduty-actions"
"pluginPackage": "@backstage/plugin-scaffolder-backend"
},
"homepage": "https://pagerduty.github.io/backstage-plugin-docs/index.html",
"repository": {
Expand Down
36 changes: 36 additions & 0 deletions plugins/backstage-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# PagerDuty Backstage Plugin

> The PagerDuty plugin reduces the cognitive load on developers responsible for maintaining services in production.
Instead of having to go to PagerDuty's console, you can now access the necessary information directly within Backstage.
This includes finding active incidents or opening a new incident, reviewing recent changes made to the service,
and checking who is on-call.

## New Frontend System (NFS)

This frontend plugin is starting the adoption of the [New Frontend System](https://backstage.io/docs/frontend-system/). This allow us to be present in Portal and keep up to date with Backstage's recommended best practices.

### Migration

We're still migrating to the NFS, since it is still in alpha. You can find this version of the plugin on the export `@pagerduty/backstage-plugin/alpha`, which allows you to start using it even in Backstage under the new `createApp` helper from `@backstage/frontend-defaults`.

### Extensions

The NFS allows for extendable plugins, which means there are parts of it that users can replace to elevate their experience. We've historically exported two components the `EntityPagerDutyCard` and the `EntityPagerDutySmallCard` to expose some PagerDuty's service information on your Backstage entities. In the NFS, we default to `EntityPagerDutyCard` as an entity card extension, which means you can still make use of the `EntityPagerDutySmallCard` through overrides.

```javascript
import PagerDutyPlugin from '@pagerduty/backstage-plugin/alpha';
import { EntityPagerDutySmallCard } from '@pagerduty/backstage-plugin';

const OverriddenPagerDutyPlugin = PagerDutyPlugin.withOverrides({
extensions: [
PagerDutyPlugin.getExtension('entity-card:pagerduty/EntityPagerDutyCard').override({
factory: originalFactory =>
originalFactory({
params: {
loader: async () => Promise.resolve(<EntityPagerDutySmallCard />)
}
})
})
]
})
```
18 changes: 18 additions & 0 deletions plugins/backstage-plugin/dev/app-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
app:
title: Backstage Example App
baseUrl: http://localhost:3000
extensions:
# Redirect from the root to the PagerDuty page
- 'page:pagerduty':
config:
path: '/'
# Enable the small PagerDuty entity card
- 'entity-card:pagerduty/small': true
# Hide these entity cards from the catalog
- 'entity-card:catalog/has-subcomponents': false
- 'entity-card:catalog/depends-on-components': false
- 'entity-card:catalog/depends-on-resources': false
backend:
baseUrl: http://localhost:7007
listen:
port: 7007
Loading