Skip to content

Commit c851181

Browse files
authored
chore: enhance project maintenance and migrate to MUI v5 (#30)
This introduces several key enhancements to the project configuration and codebase, focusing on modernization and maintenance. ### Development - Added a small configuration to launch both a mini-frontend and a mini-backend to ensure with `just start` the UI is working properly, without the need of a full blown backstage installation. This allows faster feedback loop with auto-reload of the changes. ### Refactor: MUI v5 Migration - Migrated all UI components from **Material-UI v4** (`@material-ui/core`, `@material-ui/icons`) to **MUI v5** (`@mui/material`, `@mui/icons-material`). - Updated `package.json` dependencies and component imports to reflect this change. - Updated `AGENTS.md` documentation to reference the new styling library. ### Dependency Cleanup - Removed unused `dependencies` and `devDependencies` from `package.json` to improve project hygiene and reduce install time. ### Tooling & CI Enhancements - **Justfile**: - Added `audit` command (`yarn npm audit`) for security checks. - Updated `clean` command to ensure dependencies are installed first. - **Pre-commit**: Updated hooks to utilize `just lint` and `just test`, ensuring consistency between local development and pre-commit checks.
1 parent 02196be commit c851181

34 files changed

+7814
-30033
lines changed

.github/workflows/release.yaml

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
1-
name: release
1+
name: Release workflow
22
on:
33
push:
44
branches:
55
- main
6-
workflow_dispatch: {}
76
jobs:
7+
check-changes:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v4
12+
with:
13+
fetch-depth: 2 # We only need the current and the previous commit
14+
15+
- name: Check if the package.version has changed
16+
id: check_changes
17+
run: |
18+
if git diff --unified=0 HEAD^ package.json | grep '"version":'; then
19+
echo "changes=detected" >> $GITHUB_OUTPUT
20+
else
21+
echo "changes=none" >> $GITHUB_OUTPUT
22+
fi
23+
24+
outputs:
25+
changes: ${{ steps.check_changes.outputs.changes }}
826
release:
927
runs-on: ubuntu-latest
28+
if: needs.check-changes.outputs.changes == 'detected'
1029
defaults:
1130
run:
1231
shell: nix develop --command bash {0}
@@ -30,7 +49,6 @@ jobs:
3049
- name: Configure yarn for publishing
3150
run: |
3251
yarn config set -H 'npmAuthToken' "${{secrets.NPM_AUTH_TOKEN}}"
33-
# yarn config set -H 'npmRegistries["//npm.pkg.github.com"].npmAuthToken' "${{secrets.GITHUB_TOKEN}}"
3452
- name: Release to GitHub releases
3553
run: errout=$(mktemp); gh release create $(cat package.json | jq -r .version) -R $GITHUB_REPOSITORY -t $(cat package.json | jq -r .version) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi
3654
env:

.pre-commit-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
fail_fast: true
2+
repos:
3+
- repo: https://github.com/pre-commit/pre-commit-hooks
4+
rev: v6.0.0
5+
hooks:
6+
- id: trailing-whitespace
7+
- id: end-of-file-fixer
8+
- id: check-yaml
9+
- id: check-added-large-files
10+
- repo: local
11+
hooks:
12+
- id: lint
13+
name: run linter
14+
entry: just lint
15+
language: system
16+
pass_filenames: false
17+
- id: test
18+
name: run tests
19+
entry: just test
20+
language: system
21+
pass_filenames: false

.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

AGENTS.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Repository Guidelines
2+
3+
## Project Purpose & Structure
4+
This repository contains the **Sysdig Plugin for Backstage**, a frontend plugin that integrates Sysdig Secure vulnerability and posture reports into the Backstage service catalog.
5+
6+
**Key Directories:**
7+
- **`src/`**: Source code root.
8+
- **`api/`**: `SysdigApiClient` implementation using `fetchApi`.
9+
- **`components/`**: React components.
10+
- `SysdigComponent`: Main dashboard.
11+
- `Sysdig*FetchComponent`: Individual data widgets (Runtime, Registry, Pipeline, Posture).
12+
- **`lib/`**: Helpers, constants, and annotation definitions (`annotations.ts`).
13+
- **`dev/`**: Local development harness using `createDevApp` and mock data.
14+
- **`img/`**: Assets for documentation.
15+
16+
**Tech Stack:** React, TypeScript, Backstage Plugin API, Material UI.
17+
**Package Manager:** Yarn.
18+
19+
## Build, Test, & Development
20+
- **`just install`**: Install dependencies.
21+
- **`just start`**: Starts the local development server at `http://localhost:3000`. Uses `dev/index.tsx` for context. If address is still in use use `fuser -k 3000/tcp`.
22+
- **`just test`**: Runs unit tests using Jest.
23+
- **`just lint`**: Runs ESLint to check code quality.
24+
- **`just build`**: Builds the plugin for distribution.
25+
- **`just bump`**: Updates dependencies (requires Nix/Just).
26+
27+
## Onboarding & Known Issues
28+
**Crucial setup details for new contributors:**
29+
1. **Package Manager**: Strictly use **Yarn** (wrapped via `just`).
30+
2. **Dev Dependencies**: If `just start` fails, ensure `react`, `react-dom`, and `react-router-dom` (v6) are explicitly in `devDependencies`.
31+
3. **Configuration**: `app-config.yaml` is not committed but required for `just start`. Create it with standard proxy settings if missing.
32+
4. **Entity Context**: The plugin crashes if run in isolation because it uses `useEntity`. The `dev/index.tsx` **must** wrap `SysdigPage` in an `<EntityProvider>` with a valid mock entity object.
33+
5. **Data & Auth**: Without `SYSDIG_SECURE_TOKEN`, requests fail. For UI work, override `sysdigApiRef` with a **Mock Client** in `dev/index.tsx` (see `dev/MockSysdigClient.ts`).
34+
6. **Test Execution**: `yarn test` defaults to watch mode and will hang indefinitely. **Always use `just test`** (or `yarn test --watchAll=false`) for a single-run execution.
35+
36+
## Coding Style & Naming
37+
- **Language**: TypeScript (`.ts`, `.tsx`). Strict mode enabled.
38+
- **Components**: Functional components with Hooks (`useEntity`, `useAsync`, `useApi`).
39+
- **Styling**: Use Material UI (`@mui/material`) and Backstage core components (`@backstage/core-components`).
40+
- **Naming**: PascalCase for components (`SysdigComponent`), camelCase for functions/vars.
41+
- **Imports**: Prefer relative imports within `src/` but use strict package imports for external deps.
42+
43+
## Testing Guidelines
44+
- **Framework**: Jest + React Testing Library + MSW (Mock Service Worker).
45+
- **Location**: Tests are co-located with source files (e.g., `SysdigComponent.test.tsx`).
46+
- **Requirement**: Components must be tested for rendering and error states. Use `setupRequestMockHandlers` for API mocking.
47+
48+
## Commit & PR Guidelines
49+
- **Commits**: Follow **Conventional Commits** format:
50+
- `feat(scope): description`
51+
- `fix(scope): description`
52+
- `chore: description`
53+
- **Pull Requests**:
54+
- Title must match the commit format.
55+
- Include screenshots for UI changes.
56+
- Ensure `just test` and `just lint` pass before requesting review.
57+
58+
## Release Process
59+
The release process is semi-automated:
60+
1. **Manual Version Bump**: Before creating a release, the `version` field in `package.json` must be manually updated (e.g., from `1.3.2` to `1.3.3`).
61+
2. **Automated Release**: Once the version is manually updated and pushed to the `main` branch, the GitHub Actions workflow (`.github/workflows/release.yaml`) automatically:
62+
* Creates a new GitHub Release with the updated version.
63+
* Publishes the package to NPM.
64+
65+
## Documentation Maintenance
66+
- Keep `README.md` updated with new annotations or configuration options.
67+
- Maintain this `AGENTS.md` to reflect architectural changes.
68+
- **Agent Note**: If `src/lib/annotations.ts` changes, update the "How to annotate services" section in `README.md`.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Please refer to the [official documentation](https://docs.sysdig.com/en/docs/adm
2222

2323
### Install the package
2424

25-
#### Via NPM
25+
#### Via Yarn
2626

2727
```bash
2828
# From your Backstage root directory
@@ -115,7 +115,7 @@ metadata:
115115

116116
# VM Pipeline
117117
sysdigcloud.com/image-freetext: ghcr.io/sysdiglabs
118-
118+
119119
# Posture
120120
sysdigcloud.com/resource-name: sock-shop-carts
121121
sysdigcloud.com/resource-type: "Deployment"

app-config.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
app:
2+
title: Sysdig Plugin Dev
3+
baseUrl: http://localhost:3000
4+
5+
backend:
6+
baseUrl: http://localhost:7007
7+
listen:
8+
port: 7007
9+
10+
proxy:
11+
endpoints:
12+
'/sysdig':
13+
target: ${SYSDIG_SECURE_ENDPOINT}
14+
changeOrigin: true
15+
allowedMethods: ['GET']
16+
headers:
17+
"Authorization": "Bearer ${SYSDIG_SECURE_TOKEN}"
18+
"Content-Type": "application/json"
19+
"Accept": "application/json"
20+
"X-Sysdig-Product": "SDS"
21+
22+
sysdig:
23+
endpoint: ${SYSDIG_SECURE_ENDPOINT}

dev/index.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import { createDevApp } from '@backstage/dev-utils';
3+
import { sysdigPlugin, SysdigPage } from '../src/plugin';
4+
import { EntityProvider } from '@backstage/plugin-catalog-react';
5+
import { createApiFactory, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';
6+
import { sysdigApiRef, SysdigApiClient } from '../src/api';
7+
8+
const mockEntity = {
9+
apiVersion: 'backstage.io/v1alpha1',
10+
kind: 'Component',
11+
metadata: {
12+
name: 'sock-shop-carts',
13+
annotations: {
14+
// Un-comment to see different results in the UI
15+
//
16+
// 'sysdigcloud.com/kubernetes-cluster-name': 'sock-shop-cluster',
17+
// 'sysdigcloud.com/kubernetes-namespace-name': 'sock-shop',
18+
// 'sysdigcloud.com/kubernetes-workload-name': 'sock-shop-carts',
19+
// 'sysdigcloud.com/kubernetes-workload-type': 'deployment',
20+
// 'sysdigcloud.com/registry-vendor': 'harbor',
21+
// 'sysdigcloud.com/registry-name': 'registry-harbor-registry.registry.svc.cluster.local:5443',
22+
// 'sysdigcloud.com/image-freetext': 'ghcr.io/sysdiglabs',
23+
// 'sysdigcloud.com/resource-name': 'sock-shop-carts',
24+
// 'sysdigcloud.com/resource-type': 'Deployment',
25+
},
26+
},
27+
spec: {
28+
type: 'service',
29+
lifecycle: 'experimental',
30+
owner: 'team-c',
31+
},
32+
};
33+
34+
createDevApp()
35+
.registerApi(
36+
createApiFactory({
37+
api: sysdigApiRef,
38+
deps: { configApi: configApiRef, fetchApi: fetchApiRef },
39+
factory: ({ configApi, fetchApi }) =>
40+
new SysdigApiClient({ configApi, fetchApi }),
41+
}),
42+
)
43+
.registerPlugin(sysdigPlugin)
44+
.addPage({
45+
element: (
46+
<EntityProvider entity={mockEntity}>
47+
<SysdigPage />
48+
</EntityProvider>
49+
),
50+
title: 'Sysdig',
51+
path: '/sysdig',
52+
})
53+
.render();

dev/proxy-server.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* eslint-disable no-console */
2+
const express = require('express');
3+
const { createProxyMiddleware } = require('http-proxy-middleware');
4+
5+
const app = express();
6+
const PORT = 7007;
7+
8+
// Configuration from environment variables
9+
const target = process.env.SYSDIG_SECURE_ENDPOINT;
10+
const token = process.env.SYSDIG_SECURE_TOKEN;
11+
12+
if (!target || !token) {
13+
console.error('\x1b[31m%s\x1b[0m', 'Error: SYSDIG_SECURE_ENDPOINT and SYSDIG_SECURE_TOKEN environment variables must be set.');
14+
console.log('Usage: SYSDIG_SECURE_ENDPOINT=... SYSDIG_SECURE_TOKEN=... node dev/proxy-server.js');
15+
process.exit(1);
16+
}
17+
18+
// Enable CORS for the frontend (port 3000)
19+
app.use((req, res, next) => {
20+
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
21+
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Sysdig-Product");
22+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
23+
next();
24+
});
25+
26+
// Proxy Middleware
27+
app.use('/api/proxy/sysdig', createProxyMiddleware({
28+
target: target,
29+
changeOrigin: true,
30+
pathRewrite: {
31+
'^/api/proxy/sysdig': '', // Remove the prefix when forwarding
32+
},
33+
headers: {
34+
"Authorization": `Bearer ${token}`,
35+
"Content-Type": "application/json",
36+
"Accept": "application/json",
37+
"X-Sysdig-Product": "SDS"
38+
},
39+
onProxyRes: (proxyRes, req, _res) => {
40+
console.log(`[Proxy] ${req.method} ${req.path} -> ${target} [${proxyRes.statusCode}]`);
41+
},
42+
onError: (err, req, res) => {
43+
console.error(`[Proxy Error] ${err.message}`);
44+
res.status(500).send('Proxy Error');
45+
}
46+
}));
47+
48+
app.listen(PORT, () => {
49+
console.log(`\x1b[32mSysdig Mock Backend running on http://localhost:${PORT}\x1b[0m`);
50+
console.log(`Forwarding /api/proxy/sysdig to ${target}`);
51+
});

dev/start-dev.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* eslint-disable no-console */
2+
const { spawn } = require('child_process');
3+
const path = require('path');
4+
5+
console.log('\x1b[36m%s\x1b[0m', 'Starting Sysdig Plugin Dev Environment...');
6+
7+
// 1. Start the Proxy Server
8+
const proxyPath = path.join(__dirname, 'proxy-server.js');
9+
const proxy = spawn('node', [proxyPath], {
10+
stdio: 'inherit',
11+
env: process.env
12+
});
13+
14+
// 2. Start the Backstage Frontend
15+
// We use 'yarn backstage-cli' directly to ensure we use the local version
16+
const app = spawn('yarn', ['backstage-cli', 'package', 'start'], {
17+
stdio: 'inherit',
18+
env: process.env
19+
});
20+
21+
// Handle cleanup
22+
const cleanup = () => {
23+
console.log('\n\x1b[36m%s\x1b[0m', 'Shutting down processes...');
24+
proxy.kill();
25+
app.kill();
26+
process.exit();
27+
};
28+
29+
process.on('SIGINT', cleanup);
30+
process.on('SIGTERM', cleanup);
31+
32+
// If one fails, kill the other?
33+
proxy.on('close', (code) => {
34+
if (code !== 0 && code !== null) {
35+
console.error(`Proxy server exited with code ${code}`);
36+
cleanup();
37+
}
38+
});

flake.nix

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,23 @@
2525
devShells.default =
2626
with pkgs;
2727
mkShell {
28-
packages =
29-
[
30-
gh
31-
jq
32-
nodejs
33-
typescript
34-
yarn-berry
35-
(python3.withPackages (p: with p; [ gyp ]))
36-
]
37-
++ (with nodePackages; [
38-
typescript-language-server
39-
node-gyp
40-
]);
28+
packages = [
29+
gh
30+
jq
31+
just
32+
nodejs
33+
pre-commit
34+
typescript
35+
yarn-berry
36+
(python3.withPackages (p: with p; [ gyp ]))
37+
]
38+
++ (with nodePackages; [
39+
typescript-language-server
40+
node-gyp
41+
]);
42+
shellHook = ''
43+
pre-commit install
44+
'';
4145
};
4246

4347
formatter = pkgs.nixfmt-rfc-style;

0 commit comments

Comments
 (0)