Contributor Workflow¶
Headlamp follows a standard GitHub fork/PR workflow with a few project-specific conventions.
Before you write code¶
For non-trivial changes — new features, architectural changes, new APIs — open a GitHub issue first. Maintainers will give direction before you spend time writing code.
Small bug fixes and documentation improvements can go straight to a PR.
Setup¶
# fork on GitHub, then:
git clone https://github.com/YOUR_USERNAME/headlamp
cd headlamp
git remote add upstream https://github.com/kubernetes-sigs/headlamp
git checkout -b feat/my-feature
Keep your branch rebased on upstream/main:
Development cycle¶
# 1. Build and run
npm run backend:build
npm run backend:start # terminal 1
npm run frontend:start # terminal 2
# 2. Make your changes
# 3. Test
npm run backend:test
npm run frontend:test
# 4. Lint and format
npm run backend:lint
npm run backend:format
npm run frontend:lint
# 5. Commit with DCO sign-off (required)
git commit -s -m "feat(helm): add service proxy timeout config"
DCO (Developer Certificate of Origin)¶
Every commit must be signed off with -s:
To configure git to always add sign-off:
Commit message format¶
| Type | When |
|---|---|
feat |
New feature |
fix |
Bug fix |
docs |
Documentation only |
refactor |
Code change that isn't a feature or fix |
test |
Adding or fixing tests |
chore |
Build, CI, dependency changes |
Scopes: helm, plugins, frontend, backend, chart, auth, oidc, etc.
Writing tests¶
Go packages have *_test.go files alongside each *.go file. Use table-driven tests:
func TestServiceProxyURL(t *testing.T) {
tests := []struct {
name string
namespace string
service string
path string
want string
}{
{"basic", "default", "my-svc", "api/charts", "/serviceproxy/default/my-svc/api/charts"},
// ...
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := buildProxyURL(tt.namespace, tt.service, tt.path)
if got != tt.want {
t.Errorf("got %q, want %q", got, tt.want)
}
})
}
}
Frontend components require both a Jest test and a Storybook story:
Opening the PR¶
- Push your branch:
git push origin feat/my-feature - Open a PR against
kubernetes-sigs/headlamp:main - Fill out the PR template — include: what changed, why, how to test
- Link the related issue:
Fixes #123 - CI runs lint, tests, and build checks automatically
Contribution ideas (SPIFFE/Kubernetes focus)¶
These are high-value gaps that map well to platform engineering experience:
| Idea | What it involves |
|---|---|
| SPIRE visualization plugin | List SpiffeEntry CRDs, graph SVID expirations, show attestation status per pod. Pure plugin work — no backend changes needed |
| ESO topology plugin | Visualize SecretStore → ExternalSecret dependency graphs; show sync status and error messages |
| Velero plugin | Backup/restore workflow UI, backup location status, SVID-based BSL authentication display |
| App Catalog non-OIDC fix | Fix issue #4788 — the service proxy Authorization header forwarding bug with token auth |
| Flux plugin improvements | HelmRelease visualization, reconciliation status, source graph — the existing plugin has room to grow |
| FIPS-compliant image | Build Headlamp with GOFIPS140 mode and publish a FIPS-tagged image variant |
| Multi-cluster SVID comparison | Cross-cluster view showing SVID TTLs and trust domains across all managed clusters |
Code conventions¶
Go¶
- Packages go in
backend/pkg/. Thecmd/directory is for wiring only. - Each package ships a
*_test.gofile — no test files incmd/. - Run
npm run backend:formatbefore committing. The CI usesgofmt -sandgoimports. - Use the project's error wrapping conventions:
fmt.Errorf("context: %w", err).
TypeScript / React¶
- Components in
frontend/src/components/. Keep each component in its own directory withindex.tsx,*.test.tsx, and*.stories.tsx. - Import from
@kinvolk/headlamp-plugin/libin plugin code — do not import fromfrontend/src/directly. - Prefer functional components and hooks over class components.
- MUI v5 — use
sxprop for one-off styles; create theme overrides for systemic changes.