Contributing

This document is the single source of truth for contribution guidelines.

Toolchain#

Prerequisites#

  • Go 1.25+ (the project uses go tool for managed tool dependencies)

Common commands#

CommandDescription
go buildBuild the sew binary
go run . createBuild and run in one step
task testRun all tests (gotestsum)
task lintRun Go linter (revive)
task fmt:yamlFormat YAML files in registry/ and root
task lint:yamlCheck YAML formatting (CI-safe, no writes)
task validateValidate all registry sew.yaml files against the schema
task site:generateGenerate Hugo site content from the registry
task site:serveGenerate content and start the Hugo dev server
task site:buildGenerate content and build the Hugo site for production

When adding or modifying tasks in taskfile.yml, please update the table above to keep it in sync.

Always run go tool task lint and go tool task test before submitting any change.

The generated files under site/content/registry/ and site/static/ are gitignored and rebuilt by CI. When your changes touch files under registry/ or site/ (layouts, templates, CSS, config, the generator itself), run task site:serve to verify the site renders correctly.

Commit conventions#

This project follows Conventional Commits.

Format#

<type>: <subject>
  • type — one of feat, fix, docs, refactor, style, test, chore
  • subject — lowercase, imperative mood, no trailing period

Examples#

feat: add DNS wildcard support
fix: expand env vars in fromFile paths
docs: document values deep merge behavior
refactor: reorganize APIM registry under oss/ee groups
style: improve terminal output

Rules#

  • Keep the subject line concise (≤72 characters).
  • Use a blank line before the body when a longer explanation is needed.
  • Do not add AI-attribution footers (e.g. “Generated by…” or “Co-authored-by: AI”).

Registry structure#

The registry is a tree of context directories under registry/, following the convention org/edition/product/variant:

registry/
├── elastic/
│   └── elasticsearch/          # standalone context
├── gravitee.io/
│   ├── oss/
│   │   └── apim/
│   │       ├── base/           # abstract shared config
│   │       ├── dbless/
│   │       ├── gateway/
│   │       ├── mongodb/        # concrete variant
│   │       └── postgres/       # concrete variant
│   └── ee/
│       ├── apim/
│       │   └── kafka/
│       │       ├── base/       # abstract shared config
│       │       ├── mongodb/
│       │       └── postgres/
│       └── edge-stack/         # Ambassador Edge Stack
├── kafka/
│   └── standalone/
├── mongodb/
│   └── standalone/
└── postgresql/
    └── standalone/

Files in a context directory#

FilePurpose
sew.yamlComponent definitions, Helm repos, Kind config, features, images
sew--{flag}.yamlContext flag patch file (optional, see Context flags)
README.mdHuman-readable documentation with Hugo YAML frontmatter
notes.createGo text/template rendered after sew create (connection info)
.defaultPoints to the default child variant (one variant name per file)

.default files#

When a user specifies a partial path (e.g. gravitee.io/oss/apim), sew walks .default files to resolve the full path. Each .default file contains a single line with the name of the default child directory:

# registry/gravitee.io/oss/apim/.default
postgres

Authoring a good context#

README.md#

Write a README.md with YAML frontmatter and clear sections:

---
title: "Product Name - Variant"
description: "One-line summary of what this context deploys"
tags: [relevant, tags]
---

Include Usage (the sew create command), Endpoints (table of service URLs), and Dependencies (list of composed contexts).

If the context uses DNS (directly or via a parent with features.dns.enabled: true), add a Prerequisites section before Endpoints explaining that sew setup dns must be run after creating the cluster, that it may require sudo, and linking to the Networking guide.

Concrete context READMEs must be self-contained. Abstract contexts don’t get pages on the documentation site, so linking to a parent abstract README produces broken links. Inline any relevant documentation from the abstract parent directly into each concrete variant’s README.

Tags#

  • Every context README must have at least one tag.
  • Do not use product or organization names as tags (e.g. kafka, mongodb, gravitee, elasticsearch). Tags describe what the context does, not which product it uses — the registry path already encodes the product.

Icon#

Place an icon.svg file in the context directory (or any ancestor directory) to display a product logo on registry cards and detail pages.

  • SVG format, square aspect ratio recommended.
  • Icons inherit from parent directories: a single icon.svg at registry/org/ covers all contexts under that path.
  • When no icon is found anywhere in the ancestor chain, a generic fallback icon is rendered.

notes.create#

Write a notes.create template that tells the user what is ready and how to connect. Use Go template syntax with the available context data:

Your cluster "{{ .Kind.Name }}" is ready.

Service A   http://localhost:30080
Service B   http://localhost:30081

When a context defines context flags, use hasFlag to conditionally show endpoints that are disabled by a flag:

{{ if not (hasFlag "no-portal") -}}
Portal       http://localhost:30081
{{ end -}}

hasFlag returns true when the user passed the named flag on the CLI (e.g. --no-portal). It always returns false during sew delete (which has no flag context).

Images#

  • Use slim base images (Alpine, distroless) whenever possible.
  • Pin image tags — avoid :latest in production contexts.
  • List images in images.preload.refs so they are pulled into the Kind node before Helm installs, reducing startup time:
images:
  preload:
    refs:
      - org/image:tag

Startup optimization#

  • Use minimal resource requests suitable for local development.
  • Disable persistence for dev-oriented setups (e.g. persistence.enabled: false).
  • Disable unnecessary features to reduce resource usage and startup time.

Composition with from#

Reuse existing building blocks (databases, message brokers) instead of duplicating configuration. The from field composes one or more parent contexts:

from:
  - mongodb/standalone
  - elastic/elasticsearch/standalone
  - gravitee.io/oss/apim/base

Abstract contexts#

Use abstract: true for shared base configurations that should not be deployed directly.

Abstract contexts are meant to be composed into concrete variants via from:

abstract: true

helm:
  repos:
    - name: graviteeio
      url: https://helm.gravitee.io

components:
  - name: apim
    type: helm
    namespace: gravitee
    helm:
      chart: graviteeio/apim
      values:
        # shared defaults...

Dependencies between components#

Declare requires with conditions.ready: true when a component depends on another being fully running before it can start:

components:
  - name: apim
    requires:
      - component: mongodb
        conditions:
          ready: true
        selector:
          matchLabels:
            app: mongodb
        timeout: 15m

Helm values#

  • Use valueFiles for large Helm value overrides that would clutter inline YAML.
  • Use inline values for small, context-specific tweaks.

Optional resources#

Use onMissing: ignore for optional resources like license keys that may not exist on every developer’s machine:

components:
  - name: license
    type: k8s
    namespace: gravitee
    k8s:
      secrets:
        - name: gravitee-license
          fromFile: '$HOME/opt/gravitee/license.key'
          onMissing: ignore

Context flags#

Context flags let users customize a deployment without requiring a separate context directory for every combination. Define a flag by creating a sew--{flag-name}.yaml patch file alongside the context’s sew.yaml:

# sew--no-portal.yaml
description: "Disable the developer portal UI"
components:
  - name: apim
    helm:
      values:
        portal:
          enabled: false

Naming convention: flag names must be lowercase kebab-case (^[a-z0-9]+(-[a-z0-9]+)*$). The description field is required and is displayed on the registry site and in validation output.

Inheritance: flags placed on an abstract context are automatically inherited by every concrete context that composes from it via from. A child can override an inherited flag by providing its own file with the same name.

Flags can also fully exclude a component from deployment by setting enabled: false. Any requires entries referencing a disabled component are silently dropped:

components:
  - name: elasticsearch
    enabled: false

When to use flags vs separate contexts:

  • Can a user toggle this on or off without changing the stack’s identity? Use a flag. Examples: --no-es disables Elasticsearch, --no-portal hides the portal UI. The stack is still “APIM with Postgres” regardless.
  • Does this change the storage backend, networking model, or deployment topology? Use a separate context directory. Examples: mongodb/ vs postgres/ (different databases), dbless/ vs gateway/ (fundamentally different gateway modes).
  • Is there shared config used by multiple sibling variants? Extract it into an abstract base (abstract: true) and have variants compose from it via from. Example: oss/base/ holds the shared Helm repo, component skeleton, and port mappings; oss/mongodb/ and oss/postgres/ both extend it.
  • Does a feature layer apply across multiple existing contexts? Create a composable abstract context that stacks on top via from. Example: ee/kafka/base/ adds Kafka and license handling; ee/kafka/mongodb/ and ee/kafka/postgres/ compose it with the corresponding OSS DB context.
  • When in doubt: prefer a flag. Flags are cheaper to add, don’t create new directories, and inherit automatically through from. A flag can always be promoted to a separate context later if the divergence grows.

Users activate flags on the command line:

sew create --from gravitee.io/oss/apim --no-portal --no-ui

Schema#

The JSON Schema for sew.yaml lives at schema/sew.schema.yaml.

Validate your context files against it with sew validate (or go run . validate) and always keep it in sync when modifying config structs in internal/config/.

# Validate a single file
sew validate registry/kafka/standalone/sew.yaml

# Validate all contexts in the registry
sew validate registry/