Contributing

Prerequisites

  • Go 1.26+
  • Python 3.10+ with pip (compatibility tests)
  • Docker (optional — Lambda runtime and integration tests)
  • pre-commit (optional but recommended)
  • Hugo extended 0.165+ (optional — only to preview this documentation as a site)

No C toolchain and no SQLite headers: the SQLite driver is pure Go, which is why everything here builds with CGO_ENABLED=0.

Setup

git clone https://github.com/skyoo2003/devcloud.git
cd devcloud
make build   # builds dist/devcloud and dist/codegen
make run     # starts the server on port 4747

Make Targets

CommandDescription
make buildBuild both binaries into dist/devcloud and codegen
make runRun the server from source (go run ./cmd/devcloud)
make testRun all Go tests with CGO_ENABLED=0 and -v
make test-compatInstall tests/compatibility/requirements.txt and run the boto3 suite
make codegenRegenerate internal/generated/ from every model in smithy-models/
make codegen-s3Same, restricted to S3 — fast loop while editing templates
make docker-buildBuild the image from docker/Dockerfile as devcloud/devcloud
make docker-runRun that image on port 4747 with ./data mounted
make docs-servePreview this documentation locally with Hugo
make docs-buildBuild the documentation site and check every internal link
make cleanDelete dist/, data/, and the Hugo output in public/ and resources/
make changelog VERSION=vX.Y.ZBatch and merge Changie fragments; VERSION is required
make statsPrint registered service and operation counts

Testing

make test          # Go tests, CGO_ENABLED=0 — the same mode releases ship
make test-compat   # Python/boto3 suite in tests/compatibility/

You do not need a server running first. The devcloud_server session fixture in conftest.py starts one via go run, on a free port, against a temporary data directory it removes afterwards. Three environment variables change that:

VariableEffect
DEVCLOUD_EXTERNAL=1Connect to a server already running (e.g. in Docker) instead of starting one
DEVCLOUD_BIN=<path>Run a pre-built binary instead of go run — much faster
DEVCLOUD_PORT=<port>Use a fixed port instead of an arbitrary free one

To run the suite directly:

cd tests/compatibility
pip install -r requirements.txt
pytest -v

Code Generation

Never edit files in internal/generated/ — codegen overwrites them.

make codegen      # all services
make codegen-s3   # one service
PieceWhere
Modelssmithy-models/*.json
Sourcesinternal/codegen/source.goModelSource per format; SmithySource today
Generatorinternal/codegen/generator.go + internal/codegen/templates/
Outputinternal/generated/{service}/types.go, router.go, errors.go, base_provider.go

Providers parse the raw *http.Request themselves, so there is no generated serializer or deserializer. See architecture.md.

Reviewing the weekly model sync

smithy-sync.yml refreshes all 194 vendored models every Monday and opens a pull request. The diff is whole-tree — a measured refresh moved 93 models and 134 generated files — so do not try to read it. Read the PR body instead: it is generated by scripts/model_churn.py and lists which services gained or lost operations.

Three things to check, in order:

  1. Operations added or removed. These are the only changes that alter what DevCloud serves. Everything else is upstream reshaping traits or docs.
  2. A red ci run on the published-figure gate. Expected, not a defect: new operations move the fidelity manifest, and cmd/devcloud/coverage_test.go fails until docs/coverage.md is re-derived. Correct the figures in the sync PR — never relax the gate to make it pass.
  3. codegen-drift and compat. These must be green on their own. A red codegen-drift means the committed output does not match the models; a red compat means a real behavioural regression.

The in-job test result is printed at the top of the PR body. A failure there with a clean codegen-drift almost always means item 2.

Adding a New AWS Service

  1. Add the Smithy model to smithy-models/.
  2. Run make codegen — generates types, router, errors and stubs.
  3. Implement the provider in internal/services/<service>/provider.go. Start with the most commonly used operations; the generated base provider makes everything else return NotImplementedError.
  4. Implement the store in store.go — SQLite for relational metadata, BadgerDB for key-value, in-memory for ephemeral, filesystem for blobs.
  5. Register the plugin from an init() in register.go, and blank-import the package in cmd/devcloud/imports.go. The interface contract, error convention and config keys are in plugin-api.md.
  6. Wire startup and configcmd/devcloud/main.go and internal/config/default.yaml.
  7. Write tests — Go unit tests plus boto3 tests in tests/compatibility/.
  8. Document it — a page under docs/services/ for a core service.
internal/
├── generated/{service}/    # Auto-generated (DO NOT EDIT)
│   ├── types.go            # Request/response structs
│   ├── router.go           # Operation routing
│   ├── errors.go           # Error types
│   └── base_provider.go    # Stub (NotImplementedError)
│
└── services/{service}/     # Your implementation
    ├── provider.go         # Service logic
    ├── store.go            # Storage backend
    └── register.go         # Plugin registration

Documentation

Everything under docs/ is read two ways: as plain markdown on github.com, and as the site at https://skyoo2003.github.io/devcloud/. Hugo mounts the directory rather than copying it, so both come from the same files.

That dual audience sets one rule: no front matter. GitHub renders a YAML block as a metadata table above the page, so titles come from each document’s H1 instead, and links stay plain and relative ([coverage](coverage.md#tiers)) rather than Hugo ref shortcodes.

Adding a page takes two steps:

  1. Write docs/<name>.md starting with an # H1 — that becomes its title.
  2. Add it to [menu.before] in hugo.toml. The sidebar is defined there because Hugo would otherwise sort pages alphabetically, and every entry needs its own identifier — entries without one collapse into a single link.

Then run make docs-build. Links that leave docs/ — to source files or to root files like CONTRIBUTING.md — are rewritten to point at GitHub, which also means an unresolvable link fails silently; scripts/check-site-links.py, which make docs-build runs, is what catches it.

Code Style

  • Standard Go conventions (gofmt, go vet)
  • One responsibility per file; use existing services as patterns
  • Errors follow the AWS shape (Code, Message, StatusCode) — see plugin-api.md
  • New Go files start with // SPDX-License-Identifier: Apache-2.0. Generated files keep the DO NOT EDIT marker on line 1 and the SPDX header on line 2. The go-spdx-header pre-commit hook adds it if missing.

Pre-commit hooks

pre-commit install
pre-commit run --all-files   # optional: check the whole tree now

.pre-commit-config.yaml runs go-spdx-header, gofmt -l -w, go vet and go build on Go files — the last two with CGO_ENABLED=0, so a contributor without a C toolchain still gets them. Python under tests/ and scripts/ is handled by ruff. internal/generated/ and smithy-models/ are excluded throughout.

Linting

The full linter is not in pre-commit; CI runs it separately (lint.yml). Run it before opening a PR:

golangci-lint run --timeout=5m

License of Contributions

DevCloud is licensed under the Apache License, Version 2.0. By submitting a contribution you agree that it will be licensed under the same terms. See LICENSE and NOTICE.