This guide covers development setup and contribution workflow for the Team Operator project.
- Project Overview
- Development Setup
- Project Structure
- Making Changes
- Testing
- Pull Request Process
- Code Review Guidelines
- Releasing
- Getting Help
Team Operator is a Kubernetes operator built with Kubebuilder that automates deployment, configuration, and management of Posit Team products (Workbench, Connect, Package Manager, and Chronicle) within Kubernetes clusters.
Note: This repository is under active development and is not yet ready for production use.
Before you begin, ensure you have the following installed:
- Go 1.25+ (version specified in
go.mod) - Docker (for building container images)
- kubectl (configured to access a Kubernetes cluster)
- Kubernetes cluster (1.29+ for testing; k3d works well for local development)
- Just command runner (
brew install juston macOS, or see installation guide) - Helm (for chart development and testing)
On macOS, you may also need gsed for some Makefile targets:
brew install gnu-sedgit clone https://github.com/posit-dev/team-operator.git
cd team-operatorjust depsThis also installs git hooks that run automatically on commit.
Pre-commit hooks run automatically when you commit:
| Hook | Triggers On | What It Does |
|---|---|---|
format |
All commits | Formats Go code |
vet |
.go files |
Static analysis |
mgenerate |
api/**/*.go |
Regenerates CRDs and manifests |
helm-generate |
config/** |
Regenerates Helm chart from kustomize |
helm-lint |
dist/chart/** |
Lints Helm chart |
helm-template |
dist/chart/** |
Verifies templates render |
If a hook fails, the commit is blocked. Fix the issue, stage any generated files, and commit again.
# Run hooks manually on all files
uvx pre-commit run --all-files
# Skip all hooks
git commit --no-verify -m "message"
# Skip specific hooks only
SKIP=vet git commit -m "message"
SKIP=vet,mgenerate git commit -m "message"just buildThis compiles the operator binary to ./bin/team-operator.
To run the operator against your current Kubernetes context:
just runFor development with a local Kubernetes cluster:
# Create a k3d cluster
just k3d-up
# Install CRDs
just crds
# Run the operator
just runjust testFor tests with envtest (Kubebuilder test framework):
just mtest| Directory | Description |
|---|---|
api/ |
Kubernetes API/CRD definitions |
cmd/ |
Main operator entry point |
internal/ |
Core operator logic and controllers |
internal/controller/ |
Reconciliation controllers for each resource type |
config/ |
Kubernetes manifests and Kustomize configurations |
dist/chart/ |
Helm chart for deployment |
flightdeck/ |
Landing page dashboard component |
client-go/ |
Generated Kubernetes client code |
pkg/ |
Shared packages |
openapi/ |
Generated OpenAPI specifications |
hack/ |
Build and development scripts |
-
Create a feature branch from
main:git checkout main git pull origin main git checkout -b your-feature-name
-
Use descriptive branch names with hyphens (avoid slashes):
- Good:
add-workbench-scaling,fix-database-connection - Avoid:
feature/workbench,fix/db
- Good:
We use Conventional Commits and squash merging. Your PR title becomes the commit message and must follow this format:
<type>(<scope>): <description>
Important: PR titles are validated by CI. PRs with non-conforming titles cannot be merged. This is enforced because semantic-release uses commit messages to determine version bumps.
Types:
feat:- New feature (triggers minor version bump)fix:- Bug fix (triggers patch version bump)docs:- Documentation only changesrefactor:- Code change that neither fixes a bug nor adds a featuretest:- Adding or correcting testsbuild:- Changes to build system or dependenciesci:- Changes to CI configurationchore:- Other changes that don't modify src or test filesperf:- Performance improvementsstyle:- Code style changes (formatting, etc.)revert:- Reverts a previous commit
Breaking changes: Add ! after the type (e.g., feat!:) or include BREAKING CHANGE: in the PR body. This triggers a major version bump.
Examples:
feat(connect): add support for custom resource limits
fix(workbench): resolve database connection timeout
docs: update installation instructions
refactor(controller): simplify reconciliation logic
feat!: change API response format
-
Run formatters before committing:
just format
Or using make:
make fmt
-
Run linting:
go vet ./...
-
Follow existing patterns. New code should match the codebase.
-
Keep functions focused. Each function should do one thing well.
-
Use descriptive names. Names should reveal intent.
When adding new fields to Custom Resource Definitions:
-
Update the API types in
api/:- Add the new field with appropriate JSON tags and Kubebuilder annotations
- Include validation rules where appropriate
-
Regenerate manifests:
just mgenerate
-
Update controller logic in
internal/controller/if needed. -
Add tests for the new functionality.
-
Update Helm chart in
dist/chart/if the change affects deployment.
Example of adding a new field:
// +kubebuilder:validation:Optional
// +kubebuilder:default:=1
// Replicas is the number of instances to deploy
Replicas *int32 `json:"replicas,omitempty"`Run all unit tests:
just testOr run Go tests directly:
go test -v ./...To run tests for a specific package:
go test -v ./internal/controller/...To run a specific test:
go test -v ./internal/controller/... -run TestReconcileThe project uses Kubebuilder's envtest for integration testing:
just mtestThis sets up a local Kubernetes API server for testing without requiring a full cluster.
# Lint the chart
just helm-lint
# Render templates locally (useful for debugging)
just helm-templateAfter running tests, view coverage:
go tool cover -func coverage.out-
Ensure all tests pass:
just test -
Format your code:
just format
-
Verify no uncommitted changes from generation:
git diff --exit-code
-
Lint the Helm chart:
just helm-lint
Include the following in your PR description:
- Summary: What does this PR do?
- Motivation: Why is this change needed?
- Testing: How was this tested?
- Breaking changes: Does this introduce any breaking changes?
- Related issues: Link to any related GitHub issues
The following checks must pass:
- PR Title Check: Title must follow conventional commit format (see above)
- Build: The operator must compile successfully
- Unit tests: All tests must pass
- Kustomize: Kustomization must build without errors
- Helm lint: Chart must pass linting
- Helm template: Templates must render correctly
- No diff: Generated files must be committed
This repository uses squash and merge. Your PR title becomes the final commit message on main. The PR title format is enforced because semantic-release analyzes commit messages to determine version bumps.
- PRs require at least one approval before merging
- Address all review comments or explain disagreements
- Keep PRs focused. Smaller PRs are easier to review.
- Respond to feedback promptly
We follow specific guidelines for code review. For detailed review standards, see .claude/review-guidelines.md.
- Simplicity: Prefer explicit over clever
- Maintainability: Follow existing patterns in the codebase
- Security: Extra scrutiny for credential handling, RBAC, and network operations
API Changes (api/):
- Kubebuilder annotations are correct
- New fields have sensible defaults
- Validation rules are present
- Breaking changes have migration strategy
Controller Changes (internal/controller/):
- Reconciliation is idempotent
- Error handling reports status correctly
- Config flows from Site -> Product correctly
- Both unit and integration tests exist
Helm Chart (dist/chart/):
- Values have sensible defaults
- Templates render correctly
- RBAC permissions are minimal
- CRDs are up to date
Flightdeck (flightdeck/):
- Go templates render correctly
- Static assets are properly served
- Configuration options are documented
- Style issues handled by formatters (run
make fmt) - Personal preferences without clear benefit
- Theoretical concerns without concrete impact
Releases are automated via semantic-release when commits are merged to main. Version bumps are determined by conventional commit prefixes:
fix:→ patch (1.0.0 → 1.0.1)feat:→ minor (1.0.0 → 1.1.0)feat!:orBREAKING CHANGE:→ major (1.0.0 → 2.0.0)
The release workflow updates the changelog, tags the release, publishes the Helm chart to GHCR, and notifies downstream repos.
To manually trigger a release: gh workflow run release.yml
This repo uses roborev for continuous AI-assisted code review. roborev runs automatically on each commit and posts review feedback to pull requests.
To enable it locally, install the commit hook into your copy of the repo:
roborev install-hookOnce installed, roborev will run after each git commit and submit a review for any open PR associated with your branch.
If you have questions:
- Check existing documentation: README.md, this guide, and inline code comments
- Search existing issues: Your question may have been answered
- Open an issue: For bugs, feature requests, or questions
- Contact Posit: For production use inquiries, contact Posit
| Task | Command |
|---|---|
| Build | just build |
| Test | just test |
| Run locally | just run |
| Format code | just format |
| Regenerate manifests | just mgenerate |
| Install CRDs | just crds |
| Helm lint | just helm-lint |
| Helm template | just helm-template |
| Helm install | just helm-install |
| Create k3d cluster | just k3d-up |
| Delete k3d cluster | just k3d-down |
Thank you for contributing to Team Operator!