Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ updates:
# PRs land Monday morning; reviewing during the week; merge before next batch
interval: weekly
day: monday
# generous for 4 actions
# generous for 5 actions
open-pull-requests-limit: 5
commit-message:
# commit convention: "chore(github-actions):"
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,45 @@ jobs:
dockerfile-context: .
dockerfile-path: packages/standard-service-stub/Dockerfile
image-name: gridstream-standard-service-stub
chart-schema:
name: Chart values schema (ADR-0011)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Install uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3.2.4
with:
# major.minor floor — see CONTRIBUTING.md "Tool version pinning"
version: "0.11"
enable-cache: true

- name: Sync dev deps
# jsonschema is a root-pyproject dev dep for the schema smoke test;
# --frozen so a stale lock fails CI loudly, matching the reusable
# workflow's lockfile discipline.
run: uv sync --all-extras --dev --frozen

- name: Validate values.schema.json (ADR-0011)
run: make check-schema

# with: no working-directory set - this job runs from repo root by default
chart-behavior:
name: Chart behavior (ADR-0011)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Install Helm
# Helm doesn't have its own helm/setup-helm first-party action --
# Microsoft's K8s team filled the gap and the community settled there
# (it's the de facto standard for Helm-in-CI pipelines now). Hence `azure/`.
# This is a CI install path, nothing to do with what the action is coupled to.
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0

- name: Validate chart behavior (ADR-0011)
# Runs helm lint + 3× helm template (1 known-good, 2 known-bad).
# No uv/Python needed — pure helm.
run: make check-chart
4 changes: 3 additions & 1 deletion .github/workflows/standard-python-service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ on:
description: |
Image name without registry prefix or tag (e.g. "my-team-service",
not "ghcr.io/myorg/my-team-service:latest"). The build step
concatenates this with ":${{ github.sha }}" to produce the final
concatenates this with ":<commit-sha>" to produce the final
tag, so passing a registry-prefixed or tagged value yields an
invalid double-tag like "ghcr.io/foo:latest:abc123" that fails
at build time. Not currently enforced — see remaining_sprints.md
Expand Down Expand Up @@ -89,6 +89,8 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3.2.4
with:
# major.minor floor — see CONTRIBUTING.md "Tool version pinning"
version: "0.11"
enable-cache: true

- name: Set up Python ${{ inputs.python-version }}
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ DEVLOG/
# .python-version
# comment out is correct here - Python version is a paved-road policy decision

# macOS Finder metadata
.DS_Store

# Tee'd test result files (per Sprint1.txt §7)
packages/*/tests/results_*.txt





# Byte-compiled / optimized / DLL files
Expand Down
21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

# ─── Configuration ──────────────────────────────────────────────────────────

# single-source-of-truth
PYTHON_VERSION := $(shell cut -d. -f1,2 .python-version)

# Local Kind cluster name. Override with `make CLUSTER=foo infra-up`.
CLUSTER ?= gridstream
KIND_CONFIG ?= kind/cluster.yaml
Expand Down Expand Up @@ -57,25 +60,31 @@ test: ## Run pytest with the 80% coverage gate.
--cov-report=term-missing \
--cov-fail-under=80

# [SPRINT-1-CLEANUP] Wire-up verification: confirm `jsonschema` is in dev deps,
# helm is installed locally, then run this target end-to-end. Add the
# corresponding step to .github/workflows/ci.yml during CI review.
.PHONY: check-schema
# Validates the schema FILE is well-formed
check-schema: ## Smoke-test charts/standard-service/values.schema.json (ADR-0011).
@scripts/check-chart-schema.sh

.PHONY: check-chart
# Validates schema's BEHAVIOR — that helm actually rejects const-pinned and
# required-field violations at template/install time
check-chart: ## Verify helm enforces the values schema (ADR-0011 phase 2).
@scripts/check-chart-behavior.sh

##@ Build (ADR-0009)

.PHONY: build
build: ## Build the stub's distroless production image.
docker build \
--build-arg PYTHON_VERSION=$(PYTHON_VERSION) \
-f $(STUB_DIR)/Dockerfile \
-t $(STUB_IMAGE) \
.

.PHONY: build-dev
build-dev: ## Build the stub's slim local-dev image (NOT FOR PRODUCTION).
docker build \
--build-arg PYTHON_VERSION=$(PYTHON_VERSION) \
-f $(STUB_DIR)/Dockerfile.dev \
-t $(STUB_IMAGE)-dev \
.
Expand All @@ -95,7 +104,7 @@ infra-down: ## Tear down the local Kind cluster.
kind delete cluster --name $(CLUSTER)

.PHONY: deploy-local
deploy-local: build check-schema ## Build, kind-load, and helm-install the stub.
deploy-local: build check-schema check-chart ## Build, kind-load, and helm-install the stub.
kind load docker-image $(STUB_IMAGE) --name $(CLUSTER)
helm upgrade --install $(STUB_NAME) charts/standard-service \
--set app.name=$(STUB_NAME) \
Expand All @@ -107,3 +116,7 @@ deploy-local: build check-schema ## Build, kind-load, and helm-install the stub.
@echo " kubectl get pods -l app.kubernetes.io/instance=$(STUB_NAME)"
@echo " kubectl port-forward svc/$(STUB_NAME) 8000:80"
@echo " curl http://localhost:8000/healthz"

.PHONY: smoke-test
smoke-test: ## Smoke-test the deployed standard-service-stub.
@scripts/smoke-test.sh
62 changes: 40 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,49 +90,67 @@ Wet-bulb fail-open interlocks, critical-asset tagging, and human-in-the-loop gat

## 5. Local Development

> **Status:** Sprint 1 in progress. Commands below are the *target* state and will be live as sprints land.
> **Status:** Sprint 1 complete — paved road shipped, no traffic on it yet. The stub service exists to prove the chart and reusable workflow function end-to-end. Sprint 2 brings the producer/consumer that actually exercises the road.

### Prerequisites
- Python 3.11+ (managed via `uv` or `poetry`)
- Docker & Docker Compose
- Kind or Minikube
- Python 3.11+ via [`uv`](https://docs.astral.sh/uv/)
- Docker
- [Kind](https://kind.sigs.k8s.io/) (Kubernetes-in-Docker)
- Helm 3.0+

### Quickstart (post-Sprint 1)
### Quickstart

```bash
make setup # uv sync + install pre-commit hooks
make infra-up # Start the local Kind cluster
make deploy-local # Build the stub container, kind-load it, helm-install
```

Then:

```bash
make setup # Install deps and pre-commit hooks
make infra-up # Start Kafka, Schema Registry, Prometheus/Grafana via Docker Compose
make deploy-local # Helm-install the reference service into Kind
kubectl get pods -l app.kubernetes.io/instance=standard-service-stub
kubectl port-forward svc/standard-service-stub 8000:80
curl http://localhost:8000/healthz
```

`make help` lists every target. For the full adoption walkthrough, see [`docs/paved-road.md`](./docs/paved-road.md).

---

## 6. Repository Structure

```
.
├── .github/
── workflows/ # Reusable CI workflow templates (Sprint 1)
│ ├── CODEOWNERS
── PULL_REQUEST_TEMPLATE.md
│ └── ISSUE_TEMPLATE/
── charts/ # Paved-road Helm chart (Sprint 1)
── workflows/
├── standard-python-service.yml # Reusable – paved road's CI
── ci.yml # GridStream's caller
── charts/
│ └── standard-service/ # Paved-road Helm chart
├── docs/
│ ├── ARCHITECTURE.md
│ ├── CONTEXT.md
│ ├── CONTRIBUTING.md
│ ├── DISCOVERY.md
│ ├── adr/ # Architectural Decision Records
│ ├── paved-road.md # 10-min adoption tutorial
│ ├── adr/ # ADRs
│ └── remaining_sprints.md
├── packages/ # uv workspace members (ADR-0010)
│ ├── standard-service-stub/ # Sprint 1 — the paved road's first traveler
│ ├── producer/ # Sprint 2
│ ├── consumer/ # Sprint 2
│ └── models/ # Sprint 2 — shared Pydantic + Avro models
├── kind/
│ └── cluster.yaml # Local cluster config
├── infra/
│ └── terraform/ # AWS modules (Sprint 5, stubbed)
├── schemas/ # Avro contracts (Sprint 2)
├── src/
│ ├── producer/ # (Sprint 2)
│ └── consumer/ # (Sprint 2)
├── data/ # Sample energy CSVs
├── scripts/ # scaffold-a-service, etc. (Sprint 4)
└── Makefile
│ └── terraform/ # AWS modules (Sprint 5, stubbed)
├── schemas/ # Avro contracts (Sprint 2)
├── data/ # Sample energy CSVs (Sprint 2)
├── scripts/ # scaffold-a-service, etc. (Sprint 4)
├── Makefile
├── pyproject.toml # Workspace root (virtual; ADR-0010)
└── .pre-commit-config.yaml
```

---
Expand Down
6 changes: 6 additions & 0 deletions charts/standard-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ These have no sensible default. Helm fails fast if missing.
| `app.name` | Container name and identification surface. Logs and dashboards rely on this being explicit per service. |
| `image.repository` | The chart can't guess where your image lives. |

> The empty strings in `values.yaml` defaults aren't placeholders to fill
> in — they're the schema's rejection trigger. Tooling that runs against
> bare defaults (e.g. plain `helm lint charts/standard-service`) will
> fail; pass placeholder values to exercise chart behavior. See
> `scripts/check-chart-behavior.sh` for the canonical pattern.

### SHOULD configure

These have safe defaults, but a production-shaped service usually wants its
Expand Down
14 changes: 14 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ If you are modifying a `.avsc` file:
2. Run the compatibility checker: `make schema-check`.
3. Notify the #platform-standards Slack channel.

### Tool version pinning

Tool versions shared between CI and Dockerfiles are pinned to a `major.minor`
floor — e.g. `uv@0.11`, not `uv@0.11.8`. This allows automatic patch updates
(security and bugfix releases) without manual bumps, while breaking-version
moves remain explicit PRs that touch this floor and the lockfile in the same
diff.

The same floor string appears in both `version:` inputs to GitHub Actions
(`astral-sh/setup-uv@... { version: "0.11" }`) and `:major.minor` Docker
image tags (`COPY --from=ghcr.io/astral-sh/uv:0.11 ...`), so CI and local
builds resolve to the same family on every run. Drift between the two is
the failure mode this convention is designed to prevent.

---

## References
Expand Down
7 changes: 7 additions & 0 deletions docs/completed_sprints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# GridStream Completed Sprints

In reverse chronological order.

---

# Sprint 0: Discovery, Requirements, Architecture, Planning - DONE 4/29/2026 11:15a
Loading
Loading