GitLab logo

Backstage GitLab Plugin

Created by ImmobiliareLabs

GitLab is a complete platform for the software lifecycle. It covers source code, merge requests, issues, pipelines, releases, and security in one place. Many teams use it to plan, build, test, and ship their services.

The GitLab plugin brings that context into Backstage. It adds a GitLab tab and optional cards on your entity pages, so you can see pipelines, merge requests, issues, recent releases, project languages, code owners, coverage, and the project readme. It works with personal tokens or project tokens. It supports multiple GitLab instances, including self managed and SaaS GitLab. It handles old and new GitLab APIs. Pagination keeps large tables fast.

This helps when you want build health, PR flow, and release notes next to your service docs. It gives platform teams a consistent view across repos. It is useful for organizations with many projects or a monorepo setup. The plugin can auto detect the related project from your catalog file. You can point it to a different project when needed. You can hide specific cards for services that do not use GitLab issues. There is optional OAuth support if you prefer user based access. If you already run Backstage, this plugin makes GitLab data part of the daily workflow without context switching.

Installation Instructions

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

  1. Install the packages
Copy
# from your Backstage root
yarn --cwd packages/app add @immobiliarelabs/backstage-plugin-gitlab
yarn --cwd packages/backend add @immobiliarelabs/backstage-plugin-gitlab-backend
  1. Add your GitLab integration in app config
Copy
# app-config.yaml
integrations:
  gitlab:
    - host: gitlab.com
      token: ${GITLAB_TOKEN}
    # you can add more instances by adding more items
    # - host: gitlab.mycompany.internal
    #   token: ${GITLAB_INTERNAL_TOKEN}
  1. If you use the new Backstage backend system add the backend plugin this way
Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import {
  gitlabPlugin,
  catalogPluginGitlabFillerProcessorModule,
} from '@immobiliarelabs/backstage-plugin-gitlab-backend';

async function start() {
  const backend = createBackend();

  backend.add(gitlabPlugin);
  backend.add(catalogPluginGitlabFillerProcessorModule);

  await backend.start();
}

start().catch(err => {
  process.stderr.write(`${err}\n`);
  process.exit(1);
});

The filler processor auto fills project id and project slug annotations from your GitLab location

  1. If you use the classic backend system wire the backend plugin with an express router
Copy
// packages/backend/src/plugins/catalog.ts
import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
import { PluginEnvironment } from '../types';
import { Router } from 'express-serve-static-core';
import { GitlabFillerProcessor } from '@immobiliarelabs/backstage-plugin-gitlab-backend';

export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
  const builder = await CatalogBuilder.create(env);

  // add the filler that enriches entities with GitLab annotations
  builder.addProcessor(new GitlabFillerProcessor(env.config));

  const { processingEngine, router } = await builder.build();
  await processingEngine.start();
  return router;
}
Copy
// packages/backend/src/plugins/gitlab.ts
import { PluginEnvironment } from '../types';
import { Router } from 'express-serve-static-core';
import { createRouter } from '@immobiliarelabs/backstage-plugin-gitlab-backend';

export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
  return createRouter({
    logger: env.logger,
    config: env.config,
  });
}
Copy
// packages/backend/src/index.ts
import gitlab from './plugins/gitlab';
import { createEnv, useHotMemoize } from './lib'; // or your local helpers

async function main() {
  // other setup

  const gitlabEnv = useHotMemoize(module, () => createEnv('gitlab'));
  apiRouter.use('/gitlab', await gitlab(gitlabEnv));

  // other setup
}
  1. Add the frontend plugin to the classic Backstage frontend
Copy
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import { EntityLayout, EntitySwitch } from '@backstage/plugin-catalog';
import { Grid } from '@backstage/core-components';

import {
  isGitlabAvailable,
  EntityGitlabContent,
  EntityGitlabLanguageCard,
  EntityGitlabMergeRequestsTable,
  EntityGitlabMergeRequestStatsCard,
  EntityGitlabPeopleCard,
  EntityGitlabPipelinesTable,
  EntityGitlabReadmeCard,
  EntityGitlabReleasesCard,
} from '@immobiliarelabs/backstage-plugin-gitlab';

// add a GitLab tab on the service page
export const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route if={isGitlabAvailable} path="/gitlab" title="GitLab">
      <EntityGitlabContent />
    </EntityLayout.Route>
  </EntityLayout>
);

// add cards on the overview page
export const overviewContent = (
  <Grid container spacing={3} alignItems="stretch">
    <EntitySwitch>
      <EntitySwitch.Case if={isGitlabAvailable}>
        <Grid item md={12}>
          <EntityGitlabReadmeCard />
        </Grid>
        <Grid item sm={12} md={3} lg={3}>
          <EntityGitlabPeopleCard />
        </Grid>
        <Grid item sm={12} md={3} lg={3}>
          <EntityGitlabLanguageCard />
        </Grid>
        <Grid item sm={12} md={3} lg={3}>
          <EntityGitlabMergeRequestStatsCard />
        </Grid>
        <Grid item sm={12} md={3} lg={3}>
          <EntityGitlabReleasesCard />
        </Grid>
        <Grid item md={12}>
          <EntityGitlabPipelinesTable />
        </Grid>
        <Grid item md={12}>
          <EntityGitlabMergeRequestsTable />
        </Grid>
      </EntitySwitch.Case>
    </EntitySwitch>
  </Grid>
);

EntityGitlabContent does not load the README by default. Use EntityGitlabReadmeCard if you want the README on the overview

  1. If you use the new frontend system you can enable the alpha export
Copy
# from your Backstage root if not already installed
yarn --cwd packages/app add @immobiliarelabs/backstage-plugin-gitlab
Copy
// packages/app/src/App.tsx
import { createApp } from '@backstage/frontend-app-api';
import catalogPlugin from '@backstage/plugin-catalog/alpha';
import userSettingsPlugin from '@backstage/plugin-user-settings/alpha';
import gitlabPlugin from '@immobiliarelabs/backstage-plugin-gitlab/alpha';

export const app = createApp({
  features: [
    catalogPlugin,
    userSettingsPlugin,
    gitlabPlugin,
  ],
});

Then list the GitLab cards you want on the overview page using app extensions

Copy
# app-config.yaml
app:
  extensions:
    - entity-card:gitlab/people
    - entity-card:gitlab/languages
    - entity-card:gitlab/merge-requests-stats
    - entity-card:gitlab/releases
    - entity-card:gitlab/coverage
    - entity-card:gitlab/readme
  1. Add annotations in your entity files if you need to point to a different project or instance
Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    gitlab.com/project-id: '1234'            # wrap numeric ids in quotes
    # or use a slug
    gitlab.com/project-slug: 'group_name/project_name'
    # or pin a non default GitLab instance
    gitlab.com/instance: gitlab.internal.abcd
spec:
  type: service
  lifecycle: production
  owner: team-a

That is all you need to install the plugin on both frontend and backend. Add your token or enable OAuth or OIDC. Register backend with the new system or with the classic express router. Add the tab or the cards so users can see GitLab data in the entity pages

Things to Know

Authentication

The plugin can use a static token. It can also use OAuth or OIDC. The default is a static token from app config. OAuth or OIDC uses the user session from Backstage. That requires the GitLab auth backend provider. OAuth or OIDC works with one GitLab instance. Keep useOAuth false if you need more than one instance.

If you use a static token set it under integrations in app config. You can use a personal token or a project token. Give it read access for pipelines merge requests issues and releases. On newer GitLab versions read_api usually works. On older versions you may need api. Use the smallest scope that still loads the data you need.

Copy
# app-config.yaml
integrations:
  gitlab:
    - host: gitlab.com
      token: ${GITLAB_TOKEN}

  # add more instances if needed
  # - host: gitlab.mycompany.internal
  #   token: ${GITLAB_INTERNAL_TOKEN}

If you switch to OAuth or OIDC set this. Then install and configure the GitLab auth provider on the backend.

Copy
# app-config.yaml
gitlab:
  useOAuth: true

Multiple GitLab instances and self managed

Add one entry per instance under integrations gitlab. The host must match the instance domain. For entities that live on a non default instance add an annotation to pin the instance.

Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    gitlab.com/instance: gitlab.mycompany.internal
spec:
  type: service
  owner: team-a

How the plugin finds the project

The backend filler processor reads the entity location. If that location points to a GitLab repo it fills project id and project slug automatically. You can override with annotations. Quote numeric ids.

Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    gitlab.com/project-id: '1234'
    # or
    gitlab.com/project-slug: 'group_name/project_name'
spec:
  type: service
  owner: team-a

You can turn off GitLab views for an entity by setting empty strings.

Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: docs-site
  annotations:
    gitlab.com/instance: ''
    gitlab.com/project-slug: ''
spec:
  type: website
  owner: team-b

Backend settings

Tweak defaults in app config. Use proxySecure false if your GitLab uses a self signed cert. Enable cache to reduce API traffic. Set allowedKinds to control which kinds render GitLab data.

Copy
# app-config.yaml
gitlab:
  defaultCodeOwnersPath: .gitlab/CODEOWNERS
  defaultReadmePath: .gitlab/README.md
  allowedKinds: ['Component', 'Resource']
  proxySecure: true
  useOAuth: false
  cache:
    enabled: true
    ttl: 300

If your CODEOWNERS file lives in a different path you can set an annotation on the entity. The card will use the annotation. If missing it falls back to the default path in app config.

Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    gitlab.com/codeowners-path: 'somewhere/CODEOWNERS'
spec:
  type: service
  owner: team-a

Feature guide

The GitLab tab shows a combined view. It includes pipelines merge requests issues releases languages people and stats. The tables use pagination. Use the cards on the overview for a quick at a glance view. The releases card shows the latest releases. The merge requests stats card shows counts by state. The pipelines table shows the latest pipelines with status and duration. The people card reads CODEOWNERS and enriches users from GitLab.

Custom client for old or new GitLab versions

If your GitLab API returns a different shape extend the client on the app side. Override only what you need. Keep the setupAPI factory so the client is wired into discovery and uses your config paths.

Copy
// packages/app/src/api.ts
import { AnyApiFactory, createApiFactory } from '@backstage/core-plugin-api';
import { discoveryApiRef, configApiRef } from '@backstage/core-plugin-api';
import { GitlabCIApiRef } from '@immobiliarelabs/backstage-plugin-gitlab';
import { CustomGitlabCIClient } from './myCustomClient';

export const apis: AnyApiFactory[] = [
  createApiFactory({
    api: GitlabCIApiRef,
    deps: { configApi: configApiRef, discoveryApi: discoveryApiRef },
    factory: ({ configApi, discoveryApi }) =>
      CustomGitlabCIClient.setupAPI({
        discoveryApi,
        codeOwnersPath: configApi.getOptionalString('gitlab.defaultCodeOwnersPath'),
        readmePath: configApi.getOptionalString('gitlab.defaultReadmePath'),
      }),
  }),
];
Copy
// packages/app/src/myCustomClient.ts
import { GitlabCIClient } from '@immobiliarelabs/backstage-plugin-gitlab';

export class CustomGitlabCIClient extends GitlabCIClient {
  async getPipelineSummary(projectID: string | undefined) {
    return this.callApi(/* your custom call */);
  }
}

Common problems to avoid

Use quotes around numeric project ids in annotations. Without quotes YAML can cast them to numbers and that can break lookups.

Match the host value in integrations to the real instance hostname. If the host is wrong the backend cannot route requests.

Keep gitlab.useOAuth false if you configure more than one integration entry. OAuth or OIDC supports one instance only.

If you see cert errors against a private GitLab set proxySecure false in app config.

Changelog

This changelog is produced from commits made to the GitLab plugin since 7 months 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.

Breaking changes

  • None

Features

  • Add Backstage v1.42 support with the new frontend system in alpha. Include a GitLab page with six entity cards readme coverage releases merge requests stats people languages. Add a demo app named app next. #897 merged 1 month ago
  • Publish the alpha GitLab frontend plugin and sample app. Include entity content and the same set of cards. Add setup docs. #904 merged 1 month ago
  • Add i18n across many GitLab widgets and tables. UI text now uses translations. #748 merged 6 months ago
  • Note i18n in release notes for the alpha series. #750 merged 6 months ago

Bug fixes

  • Make MergeRequestStats labels fully localizable. #816 merged 3 months ago

Documentation

  • Update README with setup for the new frontend system in alpha. #897 merged 1 month ago
  • Add missing alpha in import examples. #902 merged 1 month ago
  • Expand README with examples and config for the alpha plugin and cards. #904 merged 1 month ago

Refactor

  • Remove the deprecated Backstage backend common package from the GitLab backend. Simplify server setup. Improve logger use. No public API changes. #817 merged 4 months ago

Chores

  • Update Backstage dependencies across packages. #797 merged 4 months ago
  • Bump versions to the 6.12.0 alpha line. Update changelogs. #826 merged 4 months ago
  • Add no private to the release publish step in CI. #903 merged 1 month ago

Set up Backstage in minutes with Roadie