Development¶
Rust checks¶
Run the checks used by CI:
cargo fmt --all --check
cargo clippy --all-targets -- -D warnings
cargo test --workspace
scripts/gen.sh
git diff --exit-code
The final command catches generated-code drift and any other uncommitted changes; inspect the diff when working in a modified tree. Generation covers STS, SQS, IAM, S3, DynamoDB, SSM, Secrets Manager, SNS, Lambda, EventBridge, CloudFormation, Kinesis, KMS, and CloudWatch Logs.
Moto compatibility tests¶
The repository vendors moto tests unmodified and runs them in server mode against
roto. The pinned version is in tests/moto/MOTO_VERSION.
scripts/run-moto-tests.sh test_sts
scripts/run-moto-tests.sh test_s3
scripts/run-moto-tests.sh test_dynamodb
scripts/run-moto-tests.sh test_awslambda
scripts/run-moto-tests.sh test_events
scripts/run-moto-tests.sh test_kinesis
The runner creates .venv-moto, installs the test requirements, builds roto, and
starts an ephemeral instance. It adapts upstream port-5000 URLs in a temporary
copy, gives Lambda's IAM role fixture a valid policy document, corrects the old
DynamoDB CloudFormation index schemas and optional-field assertions, and uses a one-second
SQS long poll for EventBridge delivery assertions. Vendored sources
remain unchanged. Tests requiring Docker are skipped; local Lambda executors are
covered by the SDK smokes. The runner cleans up the server afterward. Pass additional pytest arguments after
the service directory:
TARGET=moto runs the same tests against moto to distinguish upstream behavior
from roto gaps. ROTO_TEST_PORT selects a port other than 5070; TEST_TIMEOUT
changes the per-test timeout (default 30 seconds).
CI uses Python 3.12. Use a supported Python version for .venv-moto; Python 3.9's
older HTTP dependencies can time out on S3 uploads. ROTO_MOTO_VENV can select an
alternative virtual environment with the dependencies from
tests/moto/requirements.txt.
Expected failures¶
Known failures in tests/moto/expected_failures/<service>.txt are deselected in
normal runs. A green run means no regression against that baseline, not complete
service parity. Tests needing moto's in-process internals are ignored through
tests/moto/not_portable.txt.
Exclusions match complete test IDs, so excluding one test never suppresses another
test whose name starts with the same text.
Retry just the excluded tests without changing the baseline:
The audit succeeds when exclusions still fail or skip. It fails and lists tests
that now pass so they can be removed from the baseline. Missing test IDs and
collection errors also fail the audit. The weekly coverage-audit.yml workflow
runs this check for every baselined service and supports manual dispatch.
After reviewing coverage changes, regenerate a service baseline with:
Review the resulting diff: this records all current failures, so it can also hide regressions if accepted without inspection. Collection errors leave the existing baseline intact; parametrized IDs retain their full text, including spaces.
The DynamoDB SDK smoke checks account and region isolation using identical table names and item keys, then restarts roto and verifies items, indexes, TTL settings, and STS credential routing:
To refresh vendored suites from the pinned upstream version:
Adding behavior¶
Implement operations in the relevant service crate, add meaningful Rust or compatibility coverage, regenerate when model/codegen changes require it, and update the coverage baseline and book when observable behavior changes.
Minimal CRUD service example¶
roto-svc-logs is the small service example. It implements log-group create,
list, delete, retention updates, tags, and one event query. The protocol types
and dispatcher come from models/logs/service-2.json; the handwritten behavior
is in crates/roto-svc-logs/src/service.rs, with the SQLite tables and migration
in schema.rs and lib.rs. Its handlers scope each key by account and region,
use Diesel transactions for mutations, and return generated AWS response types.
Lambda connects to the log service in process and writes captured output to
/aws/lambda/<function>. Stream management, direct PutLogEvents, Insights,
and subscription features stay outside this example.
Typed SQLite access¶
All service database access uses Diesel's SQLite backend. Each service's
schema.rs declares database tables; services with stored records use
models.rs or local structs derived with Queryable, Selectable and
Insertable. Generated AWS API models remain separate. Handlers use typed
queries inside DieselDb::transaction, preserving transaction boundaries.
Path-prefix filters use SQLite substring functions so prefixes remain literal
and case-sensitive.
Store::diesel_db runs existing service migrations, configures WAL, foreign keys,
busy timeout and durability, and shares the service lock with the legacy
connection used by inspection. Ephemeral connections share an isolated in-memory
SQLite database. rusqlite remains only in the shared store for migrations and
the inspection UI's dynamic schema and row access; service handlers use Diesel.
Add a direct Diesel dependency when using its derives or table macros; the core
crate converts Diesel errors to the existing storage error response.
Python package releases¶
Install with uv tool install roto-aws; the package provides the roto-server
command. Upgrade with uv tool upgrade roto-aws. For development from source,
run cargo run -p roto-server -- --port 5070.
Push a Cargo-compatible version tag such as v0.0.1 to trigger
.github/workflows/release.yml. The shared workflow sets the workspace version
from the tag, builds Linux x64/ARM64, Windows x64, macOS Intel/ARM64 wheels and a
source distribution, then the local publishing job uploads them to PyPI.
The shared components are pinned to a commit in vivainio/actions.
Before the first release, create the GitHub environment pypi and configure a
PyPI Trusted Publisher (or pending publisher for a new project) with:
- Project:
roto-aws - Owner:
vivainio - Repository:
roto - Workflow filename:
release.yml - Environment:
pypi
Release tags should point to commits that pass the existing CI checks.
Build locally with uvx maturin build --release --locked --out dist or
uvx maturin sdist --out dist.
To retry uploading already-built distributions, run the release workflow
manually and provide the original release run ID in artifact-run-id. This
skips rebuilding and publishes that run's artifacts. Verify the chosen run
belongs to the release you intend to publish.