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 4747Make Targets
| Command | Description |
|---|---|
make build | Build both binaries into dist/ — devcloud and codegen |
make run | Run the server from source (go run ./cmd/devcloud) |
make test | Run all Go tests with CGO_ENABLED=0 and -v |
make test-compat | Install test/compatibility/requirements.txt and run the boto3 suite |
make codegen | Regenerate internal/generated/ from every model in api/smithy/ |
make codegen-s3 | Same, restricted to S3 — fast loop while editing templates |
make docker-build | Build the image from build/package/Dockerfile as devcloud/devcloud |
make docker-run | Run that image on port 4747 with ./data mounted |
make docs-serve | Preview this documentation locally with Hugo |
make docs-build | Build the documentation site and check every internal link |
make clean | Delete dist/, data/, and the Hugo output in public/ and resources/ |
make changelog VERSION=vX.Y.Z | Batch and merge Changie fragments; VERSION is required |
make stats | Print 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 test/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:
| Variable | Effect |
|---|---|
DEVCLOUD_EXTERNAL=1 | Connect 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 test/compatibility
pip install -r requirements.txt
pytest -vCode Generation
Never edit files in internal/generated/ — codegen overwrites them.
make codegen # all services
make codegen-s3 # one service| Piece | Where |
|---|---|
| Models | api/smithy/*.json |
| Sources | internal/codegen/source.go — ModelSource per format; SmithySource today |
| Generator | internal/codegen/generator.go + internal/codegen/templates/ |
| Output | internal/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 420
vendored models every Monday and opens a pull request. The diff is whole-tree — a
refresh measured at 194 models moved 93 of them and 134 generated files, and the
vendored set has more than doubled since — 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:
- Operations added or removed. These are the only changes that alter what DevCloud serves. Everything else is upstream reshaping traits or docs.
- The published figures, already re-derived. New operations move the
fidelity manifest, so
docs/coverage.mdmust move with them — the sync does that arithmetic itself and commits it. Check the before→after table at the top of the PR body against item 1: the figures should move for the same reason the operations did. APublished figures: failuremeans the change needs a sentence the tool will not invent — write it before merging, and never relax the gate to make it pass. codegen-driftandcompat. These must be green on their own. A redcodegen-driftmeans the committed output does not match the models; a redcompatmeans a real behavioural regression.
The in-job test result is printed at the top of the PR body, and a failure
there with a clean codegen-drift almost always means the published-figure gate
fired on an operation that moved — which is the signal to look, not work to do.
The PR’s own ci runs that gate again against the corrected figures, so it is
green unless something is genuinely wrong.
Re-derive by hand, for a coverage change that did not come from a sync:
DEVCLOUD_UPDATE_DOCS=1 go test ./cmd/devcloud/ -run TestUpdatePublishedFiguresAdding a New AWS Service
- Add the Smithy model to
api/smithy/. - Run
make codegen— generates types, router, errors and stubs. - Implement the provider in
internal/services/<service>/provider.go. Start with the most commonly used operations; the generated base provider makes everything else returnNotImplementedError. - Implement the store in
store.go— SQLite (internal/storage/sqlite) for anything persistent, in-memory for ephemeral, filesystem for blobs. SQLite is the only embedded database in the tree; adding a second one needs a reason in the PR. - Register the plugin from an
init()inregister.go, and blank-import the package incmd/devcloud/imports.go. The interface contract, error convention and config keys are in plugin-api.md. - Wire startup and config —
cmd/devcloud/main.goandinternal/config/default.yaml. - Write tests — Go unit tests plus boto3 tests in
test/compatibility/. - 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 registrationDocumentation
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:
- Write
docs/<name>.mdstarting with an# H1— that becomes its title. - Add it to
[menu.before]inhugo.toml. The sidebar is defined there because Hugo would otherwise sort pages alphabetically, and every entry needs its ownidentifier— 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 theDO NOT EDITmarker on line 1 and the SPDX header on line 2. Thego-spdx-headerpre-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 test/ and scripts/ is handled by ruff. internal/generated/ and
api/smithy/ 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=5mLicense 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.