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
24 changes: 24 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "Flagsmith Backstage Plugin",
"image": "mcr.microsoft.com/devcontainers/typescript-node:22",
"postCreateCommand": "yarn install",
"forwardPorts": [3000],
"portsAttributes": {
"3000": {
"label": "Plugin Preview",
"onAutoForward": "openBrowser"
}
},
"customizations": {
"vscode": {
"extensions": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
],
"settings": {
"editor.formatOnSave": true
}
}
},
"postStartCommand": "echo '🚀 Run: yarn start' && echo '📍 Then open the forwarded port 3000'"
}
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run lint
run: yarn lint

typecheck:
name: TypeScript
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: TypeScript check
run: yarn tsc

build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build package
run: yarn build:all
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ node_modules/
# Production
/build
/dist
/dist-demo
/dist-types
/lib
*.tsbuildinfo

# Misc
.DS_Store
.env
app-config.local.yaml
.env.local
.env.development.local
.env.test.local
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
164 changes: 148 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,161 @@
# flagsmith
# Flagsmith Plugin for Backstage

Welcome to the Flagsmith plugin!
Integrate [Flagsmith](https://flagsmith.com) feature flags into your Backstage instance.

This plugins:
## Features

- Adds a 'Feature Flags' tab on component pages.
- Provides 2 Cards that can be added to component Overview pages.
- **Feature Flags Tab** - View all feature flags for a service directly in the entity page
- **Overview Card** - Quick summary of flags and their states
- **Usage Card** - Display Flagsmith usage metrics

## Getting started
## Installation

Currently, it is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
### 1. Install the plugin

Add the following annotations to a component to link it to a flagsmith project, replacing with your Project and Organization IDs:
```bash
# From your Backstage root directory
yarn --cwd packages/app add @flagsmith/backstage-plugin
```

### 2. Configure the Backstage proxy

Add to your `app-config.yaml` (or `app-config.local.yaml` for local development):

```yaml
proxy:
endpoints:
'/flagsmith':
target: 'https://api.flagsmith.com/api/v1'
headers:
Authorization: Api-Key ${FLAGSMITH_API_TOKEN}
```

> **Note:** Use an environment variable for the API token in production. Never commit tokens to version control.

For self-hosted Flagsmith, change the target URL:

```yaml
proxy:
endpoints:
'/flagsmith':
target: 'https://your-flagsmith-instance.com/api/v1'
headers:
Authorization: Api-Key ${FLAGSMITH_API_TOKEN}
```
annotations:
flagsmith.com/project-id: "00000"
flagsmith.com/org-id: "00000" # Optional, defaults to first org

### 3. Add the Feature Flags tab to entity pages

In `packages/app/src/components/catalog/EntityPage.tsx`:

```typescript
import { FlagsTab } from '@flagsmith/backstage-plugin';

// Add to your entity page layout (e.g., serviceEntityPage)
<EntityLayout.Route path="/feature-flags" title="Feature Flags">
<FlagsTab />
</EntityLayout.Route>
```

### 4. (Optional) Add cards to the Overview page

```typescript
import {
FlagsmithOverviewCard,
FlagsmithUsageCard,
} from '@flagsmith/backstage-plugin';

// Add to your entity overview page
<Grid item md={6}>
<FlagsmithOverviewCard />
</Grid>
<Grid item md={6}>
<FlagsmithUsageCard />
</Grid>
```

### 5. Annotate your entities

Add Flagsmith annotations to your `catalog-info.yaml`:

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-service
annotations:
flagsmith.com/project-id: '12345'
flagsmith.com/org-id: '67890' # Optional - defaults to first organization
spec:
type: service
owner: team-a
```

Configure your credentials by adding the following to app-config.yaml (or your local override app-config.local.yaml):
## Getting your Flagsmith credentials

1. Log in to your [Flagsmith dashboard](https://app.flagsmith.com)
2. Go to **Organisation Settings** > **API Keys**
3. Create or copy your **Admin API Key**
4. Find your **Project ID** and **Organisation ID** in the URL or project settings

## Development

### Prerequisites

- Node.js 22+ (Node 24 has known ESM compatibility issues with Backstage)
- Yarn
- A Backstage application for testing

### Local Development Setup

1. Clone the repository:

```bash
git clone https://github.com/Flagsmith/flagsmith-backstage-plugin.git
cd flagsmith-backstage-plugin
```

2. Install dependencies:

```bash
yarn install
```

3. To test in a Backstage app, copy or link the plugin to your Backstage workspace's `plugins/` directory and add it as a workspace dependency.

4. Create `app-config.local.yaml` with your Flagsmith credentials (this file is gitignored).

5. Run the Backstage app:
```bash
yarn start
```

### Available Scripts

| Command | Description |
| ------------ | ---------------------------- |
| `yarn start` | Start the development server |
| `yarn build` | Build for production |
| `yarn test` | Run tests |
| `yarn lint` | Lint the codebase |

### Project Structure

```
# Backstage override configuration for your local development environment
flagsmith:
apiUrl: https://api.flagsmith.com
apiToken: yourApiToken
src/
├── components/ # React components
│ ├── FlagsTab.tsx
│ ├── FlagsmithOverviewCard.tsx
│ └── FlagsmithUsageCard.tsx
├── api/ # API client (uses Backstage proxy)
│ └── FlagsmithClient.ts
├── plugin.ts # Frontend plugin definition
└── index.ts # Package exports
```

## Contributing

Contributions are welcome! Please open an issue or submit a pull request.

## License

Apache-2.0
14 changes: 14 additions & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
app:
title: Flagsmith Plugin Dev
baseUrl: http://localhost:3000

backend:
baseUrl: http://localhost:7007
listen:
port: 7007

# Mock proxy endpoint - requests are intercepted by MSW
proxy:
endpoints:
'/flagsmith':
target: 'http://localhost:7007'
62 changes: 61 additions & 1 deletion dev/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,66 @@
import { createDevApp } from '@backstage/dev-utils';
import { flagsmithPlugin } from '../src/plugin';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { setupWorker } from 'msw';
import { PropsWithChildren } from 'react';
import { flagsmithPlugin, FlagsTab, FlagsmithOverviewCard, FlagsmithUsageCard } from '../src';
import { handlers } from './mockHandlers';

// Start MSW worker for API mocking
const worker = setupWorker(...handlers);
worker.start({
onUnhandledRequest: 'bypass',
});

// Mock entity with Flagsmith annotations
const mockEntity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'demo-service',
description: 'A demo service with Flagsmith feature flags integration',
annotations: {
'flagsmith.com/project-id': '31465',
'flagsmith.com/org-id': '24242',
},
},
spec: {
type: 'service',
lifecycle: 'production',
owner: 'guests',
},
};

// Wrapper component to provide entity context
const EntityWrapper = ({ children }: PropsWithChildren<{}>) => (
<EntityProvider entity={mockEntity}>{children}</EntityProvider>
);

createDevApp()
.registerPlugin(flagsmithPlugin)
.addPage({
element: (
<EntityWrapper>
<FlagsTab />
</EntityWrapper>
),
title: 'Feature Flags',
path: '/flagsmith',
})
.addPage({
element: (
<EntityWrapper>
<div style={{ padding: 20, display: 'flex', gap: 20, flexWrap: 'wrap' }}>
<div style={{ flex: '1 1 400px', maxWidth: 600 }}>
<FlagsmithOverviewCard />
</div>
<div style={{ flex: '1 1 400px', maxWidth: 600 }}>
<FlagsmithUsageCard />
</div>
</div>
</EntityWrapper>
),
title: 'Overview Cards',
path: '/flagsmith-cards',
})
.render();
Loading