Skip to content

Latest commit

 

History

History
116 lines (87 loc) · 9.08 KB

File metadata and controls

116 lines (87 loc) · 9.08 KB

Repository Guidelines

Project Structure & Module Organization

  • Backend (GoLang): cmd/ with pkg/ (GoLang code), and test/.
  • Frontend (TypeScript/React): web_src/ built with Vite.
  • Tooling: Makefile (common tasks), protos/ (protobuf definitions for the API), scripts/ (protobuf generation), db/ (database structure and migrations).
  • Documentation: Markdown files in docs/.
  • gRPC API implementation in in pkg/grpc/actions
  • Database models in pkg/models
  • Integration component implementations: pkg/integrations//
  • UI component mappers: web_src/src/pages/app/mappers//

Pull Request Guidelines

  • PR titles must follow Conventional Commits and include a release-type prefix: feat:, fix:, chore:, or docs: (CI enforces this).
  • All commits must include a DCO sign-off trailer (Signed-off-by: Name <email>). Use git commit -s or git commit --amend -s when creating or updating commits.

Build, Test, and Development Commands

  • Bring up dev containers: make dev.up
  • Install deps, codegen, DB: make dev.setup after dev.up (re-run when protos, Go modules, or frontend deps change). By default only superplane_dev is migrated; use DEV_SETUP_DBS="superplane_dev superplane_test" when you also need superplane_test (E2E; backend CI sets this via the environment). If go mod download / go build fail with missing files under tmp/go/pkg/mod, run make dev.clean.go.cache then make dev.setup.go (often after disk-full or interrupted downloads).
  • Start API + Vite: make dev.server (after make dev.up) — UI at http://localhost:8000; use make dev.server.fg for foreground logs
  • One-shot backend tests: make test (Golang).
  • Targeted backend tests: make test PKG_TEST_PACKAGES=./pkg/workers
  • Targeted E2E tests: make e2e E2E_TEST_PACKAGES=./test/e2e/workflows
  • For E2E test authoring, see docs/contributing/e2e-tests.md
  • For performance profiling of the dev server, see docs/contributing/profiling.md
  • After updating UI code, always run make check.build.ui to verify everything is correct
  • After editing JS code, always run make format.js to make sure that the files are consistently formatted
  • After editing Golang code, always run make format.go to make sure that files are consistently formatted
  • After updating GoLang code, always check it with make lint && make check.build.app
  • NEVER MANUALLY CREATE MIGRATION FILES. ALWAYS use make db.migration.create NAME=<name> to generate DB migrations. Always use dashes instead of underscores in the name. We do not write migrations to rollback, so leave the *.down.sql files empty. After adding a migration, run make db.migrate DB_NAME=<DB_NAME> (requires a running app container from make dev.up), where DB_NAME can be superplane_dev or superplane_test
  • When validating enum fields in protobuf requests, ensure that the enums are properly mapped to constants in the pkg/models package. Check the Proto* and *ToProto functions in pkg/grpc/actions/common.go.
  • When adding a new worker in pkg/workers, always add its startup to cmd/server/main.go, and update the docker compose files with the new environment variables that are needed.
  • After adding new API endpoints, ensure the new endpoints have their authorization covered in pkg/authorization/interceptor.go
  • For UI component workflow, see web_src/AGENTS.md
  • For new components or triggers, see docs/contributing/component-implementations.md
  • For component design guidelines and quality standards, see docs/contributing/component-design.md
  • After updating the proto definitions in protos/, always regenerate them, the OpenAPI spec for the API, and SDKs for the CLI and the UI (requires a running app container from make dev.up):
    • make pb.gen to regenerate protobuf files
    • make openapi.spec.gen to generate OpenAPI spec for the API
    • make openapi.client.gen to generate GoLang SDK for the API
    • make openapi.web.client.gen to generate TypeScript SDK for the UI

Coding Style & Naming Conventions

  • Always write clean code: work test-first by default, then keep names clear, functions focused, side effects explicit, control flow shallow, and error handling useful.
  • Tests end with _test.go
  • Always prefer early returns over else blocks when possible
  • GoLang: prefer any over interface{} types
  • GoLang: when checking for the existence of an item on a list, use slice.Contains or slice.ContainsFunc
  • When naming variables, avoid names like *Str or *UUID; Go is a typed language, we don't need types in the variables names
  • When writing tests that require specific timestamps to be used, always use timestamps based off of time.Now(), instead of absolute times created with time.Date
  • The name of the application is "SuperPlane", not "Superplane" in all user-facing text (user interfaces, emails, notifications, documentation, etc.).
  • Frontend: do not create or use web_src/src/utils/* or utils.ts files. Put shared non-React helpers in web_src/src/lib/, and put React-specific reusable logic in web_src/src/hooks/.

Database Transaction Guidelines

When working with database transactions, follow these rules to ensure data consistency:

  • NEVER call database.Conn() inside a function that receives a tx *gorm.DB parameter

    • ❌ Bad: func process(tx *gorm.DB) { user, _ := models.FindUser(id) } where FindUser calls database.Conn()
    • ✅ Good: func process(tx *gorm.DB) { user, _ := models.FindUserInTransaction(tx, id) }
  • Always propagate the transaction context through the entire call chain

    • Pass tx as the first parameter to all functions that need database access
    • If a model method is used within a transaction, create an *InTransaction() variant that accepts tx
  • Context constructors must accept tx *gorm.DB if they perform database queries

    • ❌ Bad: NewAuthContext(orgID, service) that internally calls database.Conn()
    • ✅ Good: NewAuthContext(tx, orgID, service) that uses the passed transaction
  • When creating new model methods:

    • Create both variants: FindUser() and FindUserInTransaction(tx *gorm.DB)
    • The non-transaction variant should call the transaction variant: return FindUserInTransaction(database.Conn(), ...)

Why this matters: Using database.Conn() inside transaction contexts breaks isolation, causes data inconsistency on rollback, and can lead to race conditions.

Model file layout (pkg/models)

Order declarations in each model file as follows:

  1. Struct — package constants used by the model, then the struct type
  2. ConstructorsNew… functions that build values for the model (including name/ID helpers)
  3. Getters — methods on the struct (e.g. TableName(), computed accessors)
  4. Conn wrappers — functions that call …InTransaction(database.Conn(), …) (or start a transaction with database.Conn().Transaction when the whole operation must be atomic)
  5. InTransaction methods — functions whose first parameter is tx *gorm.DB

List all conn wrappers before all …InTransaction methods (sections 4 then 5). Place private helpers after the public API in the file.

Cursor Cloud specific instructions

Environment overview

The dev environment is entirely Docker-based. The VM needs Docker installed (with fuse-overlayfs storage driver and iptables-legacy for nested container support). All build, lint, test, and run commands execute inside Docker containers via docker compose exec.

Starting the development environment

  1. make dev.up — builds the dev-base Docker image and starts containers (app, db/PostgreSQL, rabbitmq). First run builds the image (~3-5 min); subsequent runs reuse the cached image.
  2. make dev.setup — installs npm deps, downloads Go modules, runs protobuf codegen, creates and migrates both superplane_dev and superplane_test databases.
  3. make dev.server — starts air (Go hot-reload) and Vite dev server inside the app container. Health check at http://localhost:8000/health.

Key gotchas

  • The app container starts with sleep infinity by default. You must explicitly run make dev.server to start the API + UI stack.
  • All make commands must be run from the /agent/repos/superplane directory.
  • The Docker daemon must be started manually in cloud VMs: sudo dockerd &>/tmp/dockerd.log & — wait ~3-4 seconds before issuing Docker commands.
  • After Docker daemon starts, ensure the socket is accessible: sudo chmod 666 /var/run/docker.sock.
  • Owner setup is enabled by default (OWNER_SETUP_ENABLED=yes). On first UI load at http://localhost:8000, you'll be prompted to create an admin account with email/password.
  • BLOCK_SIGNUP=yes is the default, so only the owner setup flow works for account creation (no open registration).
  • If make dev.setup fails on go mod download with missing files, run make dev.clean.go.cache then make dev.setup.go.
  • The make dev.setup Makefile target already creates and migrates both superplane_dev and superplane_test databases.