Getting Started¶
This guide takes you from zero to a working sc build in about 10 minutes.
Prerequisites¶
- Python 3.14+ — check with
python --version - uv —
curl -LsSf https://astral.sh/uv/install.sh | sh - Terraform (if you're using
TerraformComponent) - Cloud CLI authenticated (e.g.
gcloud auth application-default loginfor GCP)
1. Create a project¶
2. Scaffold the project file¶
This creates two files:
my-infra/
├── config.py ← describe your stack here
├── .gitignore ← sc.state.*.json, .sc/, .dist/ added automatically
└── pyproject.toml
3. Edit config.py¶
sc init writes a minimal stub. Here's a complete example that provisions a GCP VM with Terraform and verifies it over HTTP:
from small_config import HttpTest, Project, ProjectConfig, TerraformComponent, sc
class AppConfig(ProjectConfig):
default_env = "dev" # (1)
def project(self, env: str) -> Project:
project_id = "my-gcp-project" # (2)
ssh_pub_key = sc.secret("ssh_pub_key") # (3)
infra = TerraformComponent(
name="infra",
vars={
"project_id": project_id,
"ssh_pub_key": ssh_pub_key,
},
)
return Project(
name="my-app",
env=env,
components=[infra],
tests=[
HttpTest(
name="api health",
url=infra.output("ip", transform=lambda ip: f"http://{ip}:8000"),
expect_status=200,
expect_body_contains="Hello",
),
],
)
CONFIG = AppConfig()
default_envis used when you runsc buildwithout--env.- Hard-coded here; in a real project you'd branch on
envor read from an env var. - Reads
SC_SECRET_SSH_PUB_KEYfrom the environment at deploy time.
4. Export secrets¶
Secrets are never stored in config.py or state files. Export them before running any command:
The naming rule: sc.secret("foo_bar") reads SC_SECRET_FOO_BAR. See Secrets for details.
5. Dry-run first¶
This renders your Terraform templates to .dist/infra/ and prints the commands that would run — without touching any real infrastructure. Secrets are substituted with a safe placeholder in dry-run mode.
6. Deploy¶
sc build does three things in order:
- Renders component templates to
.dist/<component>/ - Applies each component (
terraform init→terraform apply) - Runs all
HttpTestassertions and prints a result table
Tests
┏━━━━━━━━━━━━━┳━━━━━━━━┓
┃ Name ┃ Result ┃
┡━━━━━━━━━━━━━╇━━━━━━━━┩
│ api health │ PASS │
└─────────────┴────────┘
7. Tear down¶
Runs terraform destroy -auto-approve for each TerraformComponent and clears the per-env state file.
8. Use multiple environments¶
config.py receives the env string. Use it to branch on project IDs, instance sizes, or anything else:
def project(self, env: str) -> Project:
project_id = "my-gcp-prod" if env == "production" else "my-gcp-dev"
...
Run against a specific env:
Each environment keeps its own state file: sc.state.dev.json, sc.state.production.json, etc. They never interfere with each other.
What's next?¶
- Concepts — understand how components, refs, and state fit together
- Multiple Environments — deeper walkthrough of env branching and state isolation
- Secrets — naming, dry-run, and proxy behaviour
- Config Reference — all parameters for every component type
- GCP Hello World — a complete, runnable example