Skip to content

Commit 6287d8e

Browse files
feat: hm.deploy and hm.dev
1 parent 6df386c commit 6287d8e

27 files changed

Lines changed: 1369 additions & 483 deletions

.github/workflows/ci.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
test:
13+
name: pytest + ruff + mypy
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
python-version: ["3.11", "3.12"]
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
cache: pip
26+
27+
- name: Install harmont + dev extras
28+
run: pip install -e '.[dev]'
29+
30+
- name: ruff check
31+
run: ruff check .
32+
33+
- name: mypy
34+
run: mypy harmont
35+
36+
- name: pytest
37+
run: |
38+
pytest -v \
39+
--deselect tests/test_gradle.py \
40+
--deselect tests/test_haskell.py

CLAUDE.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,59 @@ Memoization scope is one `dump_registry_json` render. Two targets
192192
that both depend on `apt_base` share the same `Step`, so the v0 IR
193193
contains one apt-base step with N children — not N copies.
194194

195+
## Deployments — `@hm.deploy` and `hm.dev`
196+
197+
`@hm.deploy` is a driver-agnostic decorator that registers a function
198+
as a long-lived service. The function returns a `Deployment` value
199+
produced by a driver-specific factory; v1 ships only the local Docker
200+
driver via `hm.dev.deploy(...)`. Future cloud drivers (`hm.aws.deploy`,
201+
`hm.fly.deploy`) plug in without touching the top-level decorator.
202+
203+
```python
204+
import harmont as hm
205+
206+
@hm.deploy("hello")
207+
def hello() -> hm.Deployment:
208+
return hm.dev.deploy(
209+
image="python:3.12-alpine",
210+
cmd=["python", "-m", "http.server", "5678"],
211+
port_mapping={5678: hm.dev.port()},
212+
)
213+
214+
@hm.deploy("greeter")
215+
def greeter(hello: hm.Dep[hm.Deployment]) -> hm.Deployment:
216+
return hm.dev.deploy(
217+
image="python:3.12-alpine",
218+
cmd=["python", "-m", "http.server", "5678"],
219+
port_mapping={5678: hm.dev.port()},
220+
env={"HELLO_HOST": hello.name},
221+
)
222+
```
223+
224+
Public surface:
225+
226+
```python
227+
hm.deploy(slug=None, *, name=None) # decorator
228+
hm.Dep[T] # PEP-593 fixture marker
229+
hm.Deployment # abstract dataclass
230+
231+
hm.dev.deploy(*, image=None, from_=None, cmd=None,
232+
port_mapping=None, env=None,
233+
volumes=None, workdir=None) # -> LocalDeployment
234+
hm.dev.port() # OS-assigned host port sentinel
235+
hm.dev.LocalDeployment # concrete subclass
236+
hm.dev.dump_registry_json(*, worktree_root) # -> v0 JSON
237+
```
238+
239+
`hm.dev.port()` is only valid as a value in `port_mapping`. The host
240+
port is assigned by Docker (via `-p :<container_port>`) at `hm dev up`
241+
time; query it from another terminal with `hm dev port-of <slug>
242+
<container_port>`. Ports are fresh on every `hm dev up`.
243+
244+
The Rust CLI (`hm dev up`) shells out to `python -m harmont.dev
245+
--dump-registry` to obtain the registry JSON. Schema is at
246+
`docs/superpowers/specs/2026-05-21-hm-dev-deploy-design.md` § 1.
247+
195248
## Cache keys
196249

197250
`harmont.keygen.resolve_pipeline_keys` ports the algorithm previously

0 commit comments

Comments
 (0)