Repository navigation
docs(backstage-plugins): add a guide for building a custom portal #964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| --- | ||
| title: Building a custom portal | ||
| description: Scaffold your own copy of the OpenChoreo portal with create-portal, add plugins and re-brand it, deploy it in place of the stock image, and merge in each new release. | ||
| sidebar_position: 4 | ||
| --- | ||
|
|
||
| # Building a custom portal | ||
|
|
||
| There are three ways to run a Backstage portal for OpenChoreo: | ||
|
|
||
| | Path | Choose it when | | ||
| | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | ||
| | **The stock portal** | The portal the control-plane Helm chart deploys is enough, and you only need [configuration](../backstage-configuration.mdx). | | ||
| | **A custom portal** (this page) | You want the full OpenChoreo portal, plus your own plugins, pages or branding, as a repository you own and build. | | ||
| | **[Plugins in your own Backstage](./installing-into-existing-backstage.mdx)** | You already run a Backstage app and want to add OpenChoreo to it. | | ||
|
|
||
| A custom portal starts as a copy of the stock portal, generated by `@openchoreo/create-portal`. It is a thin Backstage app: the portal itself comes from the published `@openchoreo/backstage-portal-app` and `@openchoreo/backstage-portal-backend` packages, so the repository holds only configuration and whatever you add. You do not fork `openchoreo/backstage-plugins`. | ||
|
|
||
| :::info Available from 1.4.0 | ||
|
|
||
| `@openchoreo/create-portal` and `@openchoreo/backstage-portal-app` are first published with `1.4.0`. Earlier releases have no custom-portal path; fork the upstream repository instead. | ||
|
|
||
| ::: | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js 20 or 22, and Yarn through `corepack enable`. The generated repository pins its own Yarn release. | ||
| - `git`, so the CLI can create the initial commit that upgrades build on. | ||
| - An OpenChoreo control plane on the same release, for the portal to connect to. | ||
| - Docker and access to a container registry, to build and ship the image. | ||
|
|
||
| ## Create the portal | ||
|
|
||
| ```bash | ||
| npx @openchoreo/create-portal | ||
| ``` | ||
|
|
||
| `npm init @openchoreo/portal` runs the same CLI. It asks for a name, then: | ||
|
|
||
| 1. Writes the template into `./<name>`, with every `@openchoreo/*` package pinned to the CLI's own release. | ||
| 2. Creates `app-config.local.yaml` from the bundled example. | ||
| 3. Commits the untouched template. | ||
| 4. Runs `yarn install` and `yarn tsc`. | ||
|
|
||
| The template ships inside the CLI package, so creating a portal needs only the npm registry. | ||
|
|
||
| | Flag | Meaning | | ||
| | ----------------------- | --------------------------------------------------------------------------------------------------------------- | | ||
| | `--name <name>` | Portal name (lowercase letters, digits, dashes). Skips the prompt. | | ||
| | `--path <dir>` | Write into this directory instead of `./<name>`. | | ||
| | `--registry <url>` | Registry to install `@openchoreo/*` from. Defaults to the registry npm uses for `@openchoreo`, otherwise npmjs. | | ||
| | `--skip-install` | Skip `yarn install` and `yarn tsc`. | | ||
| | `--template-path <dir>` | Use a template directory instead of the bundled one. | | ||
|
|
||
| To pin a specific release, run that version of the CLI: `npx @openchoreo/create-portal@1.4.0`. | ||
|
|
||
| ### Using a registry mirror | ||
|
|
||
| Without `--registry`, the CLI uses the registry npm resolves `@openchoreo/*` from: `@openchoreo:registry` in your `.npmrc`, otherwise npm's `registry`. A mirror configured once in `.npmrc` therefore carries over without a flag, and so does `npm init @openchoreo/portal --registry <url>`. | ||
|
|
||
| The registry is written to the portal's `.yarnrc.yml`. If it needs authentication, add an `npmAuthToken` for the `openchoreo` scope to your user-level `~/.yarnrc.yml` rather than committing it. A plain `http://` registry is added to `unsafeHttpWhitelist` automatically, because Yarn otherwise refuses it. | ||
|
|
||
| ## What you get | ||
|
|
||
| ```text | ||
| my-portal/ | ||
| ├── packages/app/ # the frontend: feature discovery installs the portal | ||
| ├── packages/backend/ # the backend: portalBackendFeatures plus your plugins | ||
| │ └── Dockerfile # the same image build as the stock portal | ||
| ├── plugins/ # your own plugins (`yarn new`) | ||
| ├── app-config*.yaml # the stock portal's configuration, now yours | ||
| ├── templates/, examples/ # the OpenChoreo scaffolder templates | ||
| └── .openchoreo-portal.json # the release this repository started from | ||
| ``` | ||
|
|
||
| The frontend is `createApp()` with no feature list. `app.packages: all` in both app-configs turns on Backstage [feature discovery](./feature-discovery.mdx), which installs the portal from the `@openchoreo/backstage-portal-app` dependency. A `discovery.test.ts` fails if that ever stops happening, which would otherwise show up only as an empty page. | ||
|
|
||
| The backend adds `portalBackendFeatures`, the stock portal's complete backend. | ||
|
|
||
| The Portal Assistant is not included. It ships only in the stock portal image. | ||
|
|
||
| ## Run it locally | ||
|
|
||
| ```bash | ||
| cd my-portal | ||
| yarn start # frontend on :3000, backend on :7007 | ||
| ``` | ||
|
|
||
| Local settings live in `app-config.local.yaml`, which git ignores. The defaults target a local k3d install on `*.openchoreo.localhost:8080`. For any other control plane, point `openchoreo.baseUrl`, `thunder.baseUrl` and the `auth.providers.openchoreo-auth` URLs at it. | ||
|
|
||
| The sign-in client must accept the local redirect URL `http://localhost:7007/api/auth/openchoreo-auth/handler/frame`. See [Authentication](../backstage-configuration.mdx#authentication). | ||
|
|
||
| ## Add plugins | ||
|
|
||
| Optional OpenChoreo plugins, community plugins and your own plugins all install the same way: as dependencies of the portal. | ||
|
|
||
| - **Frontend.** `yarn workspace app add <package>` is the whole installation. Discovery picks up any dependency that default-exports a frontend feature from its root or `./alpha` entry point. A plugin with only named exports goes into `createApp({ features: [...] })` in `packages/app/src/App.tsx`. | ||
| - **Backend.** `yarn workspace backend add <package>`, then add `backend.add(import('<package>'))` to `packages/backend/src/index.ts`. | ||
| - **Your own.** `yarn new` scaffolds a plugin into `plugins/`. | ||
|
|
||
| ## Customize | ||
|
|
||
| Everything the portal contributes is a Backstage New Frontend System extension, so most changes are configuration: | ||
|
|
||
| ```yaml title="app-config.yaml" | ||
| app: | ||
| extensions: | ||
| # Hide a tab or page you don't use | ||
| - entity-content:openchoreo-observability/wirelogs: false | ||
| # Move a tab into another group | ||
| - entity-content:openchoreo-ci/workflows: | ||
| config: | ||
| group: development | ||
| ``` | ||
|
|
||
| To replace a piece outright, contribute an extension with the same ID from a feature you pass to `createApp({ features })`. Features listed there install after discovered ones, so yours win. See [Feature discovery](./feature-discovery.mdx#turning-extensions-off) for switching extensions off. | ||
|
|
||
| Logo, product name and brand color are runtime configuration under `app.branding`; see [Theming and branding](./theming.mdx). Favicons and the web manifest live in `packages/app/public/`. | ||
|
|
||
| Optional capabilities are switched with `openchoreo.features.*.enabled`. Keep them matching what your control plane actually runs; the Helm chart sets them for you in production. | ||
|
|
||
| ## Build and deploy | ||
|
|
||
| The template ships a `yarn.lock` seeded with the dependency set the release was tested with. The first `yarn install` trims it to what your portal uses; commit the result. Then build the image: | ||
|
|
||
| ```bash | ||
| docker build . -f packages/backend/Dockerfile \ | ||
| -t registry.example.com/acme/portal:0.1.0 | ||
| docker push registry.example.com/acme/portal:0.1.0 | ||
| ``` | ||
|
|
||
| The image replaces the stock `openchoreo-ui` image, and reads the same configuration from the chart. Point the control-plane chart at it: | ||
|
|
||
| ```bash | ||
| helm upgrade openchoreo-control-plane \ | ||
| oci://ghcr.io/openchoreo/helm-charts/openchoreo-control-plane \ | ||
| --version <release> \ | ||
| --namespace openchoreo-control-plane \ | ||
| --reuse-values \ | ||
| --set backstage.image.repository=registry.example.com/acme/portal \ | ||
| --set backstage.image.tag=0.1.0 | ||
| ``` | ||
|
|
||
| Use a new tag for every build. With an unchanged tag, nodes that already have the image keep running the old one. | ||
|
|
||
| ## Upgrade to a new release | ||
|
|
||
| All `@openchoreo/*` packages version in lockstep, so an upgrade is one version bump in `packages/app/package.json` and `packages/backend/package.json`, plus whatever skeleton changes the release made to configuration, the Dockerfile or the scaffolder templates. | ||
|
|
||
| Each release's template is published as a tag of [`openchoreo/portal-template`](https://github.com/openchoreo/portal-template), so git can merge those changes into your customizations. | ||
|
|
||
| Once, right after creating the portal, record the release you started from as the merge base. This merge changes no files: | ||
|
|
||
| ```bash | ||
| git remote add portal-template https://github.com/openchoreo/portal-template | ||
| git fetch portal-template --tags | ||
| git merge -s ours --allow-unrelated-histories v1.4.0 \ | ||
|
Comment on lines
+150
to
+157
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| -m "chore: record portal-template v1.4.0 as the upgrade base" | ||
| ``` | ||
|
|
||
| Use the release in `.openchoreo-portal.json`. Then, for each upgrade: | ||
|
|
||
| ```bash | ||
| git fetch portal-template --tags | ||
| git merge v1.5.0 | ||
| ``` | ||
|
|
||
| Conflicts appear only where you changed the same lines as the release. For `yarn.lock`, take the release's copy (`git checkout --theirs yarn.lock`) and let `yarn install` add your dependencies back. Then run `yarn install && yarn tsc && yarn test:all`, update `release` in `.openchoreo-portal.json`, and rebuild the image. | ||
|
|
||
| Upgrade the portal together with the control plane, since a portal expects the API of its own release. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: openchoreo/openchoreo.github.io
Length of output: 41295
Script executed:
Repository: openchoreo/openchoreo.github.io
Length of output: 31213
Use a shell-safe Helm version placeholder.
When copied,
--version <release>treats<release>as input redirection. The shell can fail before Helm runs, or Helm receives no version value. Define the release as a shell variable and pass it to Helm.🐛 Suggested fix