Buildkite logo

Backstage Buildkite Plugin

Created by Roadie, officially approved by Buildkite.

Buildkite is a CI and CD platform. It helps you run reliable pipelines for builds and releases. It is used by engineering teams to automate testing and delivery at scale.

The Buildkite plugin puts your pipelines inside Backstage. You can see recent builds for a service. You can watch step output in real time. You can open logs without leaving your portal. You can trigger a rebuild when needed. You can filter by branch or focus on the default branch to cut noise. When you need to go deeper, you can jump straight to the build in Buildkite.This fits a self hosted Backstage where teams want CI context next to ownership, docs, and on call info. Service owners get a fast view of build health during reviews and incidents. Platform teams can track deployments across environments from the same place. The result is fewer context switches and quicker feedback when something breaks or needs a rerun.

A list of builds in a table along with a status and retry button for each build.

Installation Instructions

These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.

Install the frontend package

Copy
yarn add @roadiehq/backstage-plugin-buildkite

Configure the proxy in app config

Add this to your app config. Keep the header value as an env var.

Copy
# app-config.yaml
proxy:
  '/buildkite/api':
    target: https://api.buildkite.com/v2/
    headers:
      Authorization: 'Bearer ${BUILDKITE_API_TOKEN}'

Add the plugin to the catalog entity page

Import the components. Place them on the entity page so users can see the builds.

Copy
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntitySwitch } from '@backstage/plugin-catalog';
import {
  EntityBuildkiteContent,
  isPluginApplicableToEntity as isBuildkiteAvailable,
} from '@roadiehq/backstage-plugin-buildkite';

// example content block for CI CD tab
export const cicdContent = (
  <EntitySwitch>
    <EntitySwitch.Case if={isBuildkiteAvailable}>
      <EntityBuildkiteContent />
    </EntitySwitch.Case>
  </EntitySwitch>
);

To limit to the default branch only, pass the flag. An entity annotation can override this.

Copy
// packages/app/src/components/catalog/EntityPage.tsx
export const cicdContent = (
  <EntitySwitch>
    <EntitySwitch.Case if={isBuildkiteAvailable}>
      <EntityBuildkiteContent defaultBranchOnly />
    </EntitySwitch.Case>
  </EntitySwitch>
);

Add this content block into your entity page layout so it renders in a tab or section that is visible in your app.

Annotate your catalog entities

Add the annotations to your catalog entity yaml.

Copy
metadata:
  annotations:
    buildkite.com/project-slug: exampleorg/exampleproject
    # optional branch filter
    buildkite.com/branch: 'main'
    # optional default branch only switch
    buildkite.com/default-branch-only: 'true'

Notes on behavior in code comments above

  • If you set buildkite.com/branch the plugin shows only that branch
  • If you set buildkite.com/default-branch-only to true the plugin shows only the default branch as reported by Buildkite
  • The branch annotation takes priority over the default branch only flag and prop

Provide the Buildkite API token

Export the token in your shell so the proxy header can read it.

Copy
export BUILDKITE_API_TOKEN=xxx-xxx-xxx

You can also set it in your process manager or container env.

Backend setup for the proxy new backend system

Ensure the proxy backend plugin is added. This enables the proxy entries from your app config at the path under api proxy.

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// other backend plugins
backend.add(import('@backstage/plugin-app-backend'));

// add the proxy backend plugin
backend.add(import('@backstage/plugin-proxy-backend'));

await backend.start();

No code changes are needed in this file beyond adding the proxy backend plugin if it is missing.

Backend setup for the proxy old backend system

If your backend uses the older pattern with createRouter and useHotMemoize, add the proxy plugin.

Create the proxy plugin wiring file if it is missing.

Copy
// packages/backend/src/plugins/proxy.ts
import { createRouter } from '@backstage/plugin-proxy-backend';
import { Logger } from 'winston';
import { PluginEnvironment } from '../types';

export default async function createPlugin(env: PluginEnvironment) {
  return await createRouter({
    config: env.config,
    logger: env.logger as Logger,
    discovery: env.discovery,
  });
}

Wire it up in the backend index so it mounts under the api path.

Copy
// packages/backend/src/index.ts
import { createServiceBuilder } from '@backstage/backend-common';
import proxy from './plugins/proxy';
// other imports omitted for brevity

async function main() {
  // create envs here as in your app
  const apiRouter = Router();

  // mount other routers

  // mount the proxy at /api/proxy
  apiRouter.use('/proxy', await proxy(proxyEnv));

  await createServiceBuilder(module)
    .addRouter('/api', apiRouter)
    .start();
}

main().catch(err => {
  process.exit(1);
});

This uses the same proxy config in app config shown earlier. The plugin will call the proxy under api proxy buildkite api.

Summary of what you should have

  • Frontend package installed
  • Proxy entries in app config
  • Buildkite content added on the entity page
  • Entity annotations present on services that use Buildkite
  • Backend has the proxy plugin enabled either with the new backend method or the old backend method

Once in place, run your app as usual. The Buildkite tab or section should render on entities that have the annotations.

Things to Know

You might rebuild each build and track build progress with this plugin.

single build view in buildkite plugin

Changelog

This changelog is produced from commits made to the Buildkite plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Improvements

  • Switch time handling to Luxon in the plugin. This can trim bundle size. It may slightly change date formatting in edge cases. #1759 merged 9 months ago

Maintenance

  • Upgrade to Backstage 1.40 for compatibility with newer apps. #1952 merged 2 months ago
  • Remove unused dependencies in the plugin. #1847 merged 7 months ago
  • Update Backstage package versions to match recent core releases. #1728 merged 10 months ago
  • Routine dependency update across plugins. #1684 merged 12 months ago

Breaking changes

  • None noted

Set up Backstage in minutes with Roadie