Skip to content

Commit eb8f9ff

Browse files
authored
Merge pull request #40 from constructive-io/feat/docs-consistency
docs: align README, DEVELOPMENT, k8s, and skill docs with current repo
2 parents 09cc142 + b7fee7a commit eb8f9ff

6 files changed

Lines changed: 168 additions & 233 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pnpm build # Recompile
129129

130130
## Key Details
131131

132-
- Functions run on `PORT=8080` (Knative default)
132+
- Each function declares its port in `handler.json` (`simple-email` 8081, `send-email-link` 8082, `knative-job-example` 8083, `python-example` 8084); the job service uses 8080
133133
- Email functions support dry-run via `SIMPLE_EMAIL_DRY_RUN` / `SEND_EMAIL_LINK_DRY_RUN`
134134
- `loadFunctionApp()` in job/service resolves modules by name (e.g. `@constructive-io/simple-email-fn`)
135135
- GraphQL clients require `GRAPHQL_URL` env var and `X-Database-Id` header

DEVELOPMENT.md

Lines changed: 18 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Local development setup for running functions against real infrastructure (Postg
44

55
## Prerequisites
66

7-
- Node.js >= 18.17.0
7+
- Node.js >= 22.0.0
88
- pnpm >= 10
99
- Docker Desktop
1010

@@ -60,7 +60,8 @@ After this you should have built artifacts in:
6060
|---------|--------|
6161
| `generated/send-email-link/dist/` | Send-email-link function server |
6262
| `generated/simple-email/dist/` | Simple-email function server |
63-
| `generated/example/dist/` | Example function server |
63+
| `generated/example/dist/` | knative-job-example function server |
64+
| `generated/python-example/dist/` | Python example function server |
6465
| `job/service/dist/` | Knative job service (worker + scheduler) |
6566
| `packages/fn-runtime/dist/` | Function runtime library |
6667
| `packages/fn-app/dist/` | Function app framework |
@@ -113,6 +114,8 @@ This runs `scripts/dev.ts` which spawns local Node processes with env vars point
113114
| **job-service** | 8080 | `job/service/dist/run.js` |
114115
| **simple-email** | 8081 | `generated/simple-email/dist/index.js` |
115116
| **send-email-link** | 8082 | `generated/send-email-link/dist/index.js` |
117+
| **knative-job-example** | 8083 | `generated/example/dist/index.js` |
118+
| **python-example** | 8084 | `generated/python-example/...` (python entrypoint) |
116119

117120
To start a single function:
118121

@@ -175,6 +178,8 @@ make dev-down # Stop Docker infrastructure
175178
| Job Service | 8080 |
176179
| simple-email | 8081 |
177180
| send-email-link | 8082 |
181+
| knative-job-example | 8083 |
182+
| python-example | 8084 |
178183

179184
## Architecture
180185

@@ -280,6 +285,8 @@ Changes to runtime packages (`packages/fn-runtime`, `packages/fn-app`) or `packa
280285
|---------|------------|
281286
| simple-email | 8081 |
282287
| send-email-link | 8082 |
288+
| knative-job-example | 8083 |
289+
| python-example | 8084 |
283290
| Job Service | 8080 |
284291
| PostgreSQL | 5432 |
285292
| Constructive Server | 3002 |
@@ -295,61 +302,15 @@ Changes to runtime packages (`packages/fn-runtime`, `packages/fn-app`) or `packa
295302

296303
## Adding a New Function
297304

298-
1. **Create the function source:**
299-
300-
```bash
301-
mkdir functions/my-function
302-
```
303-
304-
Add `functions/my-function/handler.json`:
305-
```json
306-
{
307-
"name": "my-function",
308-
"version": "1.0.0",
309-
"type": "node-graphql",
310-
"description": "What this function does",
311-
"dependencies": {}
312-
}
313-
```
314-
315-
Add `functions/my-function/handler.ts` with your function logic.
316-
317-
2. **Generate the workspace package:**
318-
319-
```bash
320-
pnpm generate
321-
pnpm install
322-
```
323-
324-
This creates `generated/my-function/` with the full package, Dockerfile, and k8s manifests (including `k8s/local-deployment.yaml`).
325-
326-
3. **Register in Skaffold** — edit `skaffold.yaml`, add to the `local-simple` profile:
327-
328-
```yaml
329-
rawYaml:
330-
- generated/simple-email/k8s/local-deployment.yaml
331-
- generated/send-email-link/k8s/local-deployment.yaml
332-
- generated/my-function/k8s/local-deployment.yaml # <- add this
333-
```
334-
335-
And add a port-forward entry:
336-
```yaml
337-
portForward:
338-
- resourceType: service
339-
resourceName: my-function
340-
namespace: constructive-functions
341-
port: 80
342-
localPort: 8083 # pick an unused port
343-
```
344-
345-
4. **Register in job-service** — edit `k8s/overlays/local-simple/job-service.yaml`, add to `JOBS_SUPPORTED` and `INTERNAL_GATEWAY_DEVELOPMENT_MAP`.
346-
347-
5. **Test:**
348-
349-
```bash
350-
make skaffold-dev # deploy
351-
pnpm test:e2e # run e2e tests
352-
```
305+
The canonical, end-to-end workflow lives in [docs/skills/adding-functions.md](./docs/skills/adding-functions.md). Quick summary:
306+
307+
1. Create `functions/<name>/handler.json` and `functions/<name>/handler.ts`.
308+
2. Register the function with the job service in `job/service/src/types.ts` (`FunctionName` union) and `job/service/src/index.ts` (`functionRegistry` entry).
309+
3. Run `pnpm generate && pnpm install && pnpm build`.
310+
311+
`pnpm generate` automatically updates `skaffold.yaml` (per-function profile + port-forwards), `k8s/overlays/local-simple/job-service.yaml` (`JOBS_SUPPORTED` and `INTERNAL_GATEWAY_DEVELOPMENT_MAP`), and `generated/functions-manifest.json`. No manual edits to those files are needed.
312+
313+
Then test with `make skaffold-dev` and `pnpm test:e2e`. See the skill doc for the unit-test, e2e-test, and CI checklist.
353314

354315
## E2E Tests
355316

README.md

Lines changed: 60 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# constructive-functions
22

3-
Functions playground for Constructive — isolated workspace for building, testing, and deploying Knative-style HTTP functions.
3+
Functions playground for Constructive — a workspace for building, testing, and deploying serverless HTTP functions backed by a Postgres-backed job queue.
4+
5+
Functions are authored in `functions/<name>/` (a `handler.ts` plus a `handler.json` manifest), generated into runnable workspace packages by `pnpm generate`, and dispatched by the job service in `job/service/`. Templates live in `templates/` (`node-graphql` and `python` are supported today).
46

57
This repo is also the source of the **Portable Functions Toolkit**: a set of `@constructive-io/fn-*` npm packages that any external repo can `pnpm add` to get the same code-gen + Docker + k8s pipeline against its own `functions/` directory. See [docs/portable-functions-toolkit.md](docs/portable-functions-toolkit.md) for the full toolkit guide.
68

@@ -20,121 +22,103 @@ pnpm fn dev # run functions as local Node proce
2022
## Quick start (this repo, dogfood)
2123

2224
```bash
23-
# Install dependencies
24-
pnpm install
25-
26-
# Build all functions
27-
pnpm build
25+
pnpm generate # generate workspace packages from handler.json manifests
26+
pnpm install # install dependencies (including generated packages)
27+
pnpm build # build all packages and functions
2828

29-
# Build Docker images locally
30-
make docker-build
31-
32-
# Build individual function images
33-
make docker-build-simple-email
29+
make docker-build # build all function Docker images
30+
make docker-build-simple-email # build a single function image
3431
make docker-build-send-email-link
3532
```
3633

3734
## Functions
3835

39-
### `@constructive-io/simple-email-fn`
36+
| Function | Port | Type | Image |
37+
|----------|------|------|-------|
38+
| `simple-email` | 8081 | node-graphql | `ghcr.io/constructive-io/constructive-functions/simple-email:latest` |
39+
| `send-email-link` | 8082 | node-graphql | `ghcr.io/constructive-io/constructive-functions/send-email-link:latest` |
40+
| `knative-job-example` | 8083 | node-graphql | `ghcr.io/constructive-io/constructive-functions/knative-job-example:latest` |
41+
| `python-example` | 8084 | python | `ghcr.io/constructive-io/constructive-functions/python-example:latest` |
4042

41-
Simple email function that sends emails directly from job payload.
43+
Port `8080` is reserved for the job service.
4244

43-
- **Port**: `8080`
44-
- **Docker image**: `ghcr.io/constructive-io/constructive-functions/simple-email:latest`
45-
- **Environment**:
46-
- `SIMPLE_EMAIL_DRY_RUN` — If `true`, logs email payload without sending
47-
- `MAILGUN_API_KEY`, `MAILGUN_KEY` — Mailgun credentials
48-
- `MAILGUN_DOMAIN`, `MAILGUN_FROM`, `MAILGUN_REPLY` — Mailgun email config
45+
### `simple-email`
4946

50-
### `@constructive-io/send-email-link-fn`
47+
Sends emails directly from a job payload.
5148

52-
Email link function for invite, password reset, and verification emails.
49+
- `SIMPLE_EMAIL_DRY_RUN` — if `true`, logs the payload instead of sending
50+
- `MAILGUN_API_KEY`, `MAILGUN_KEY`, `MAILGUN_DOMAIN`, `MAILGUN_FROM`, `MAILGUN_REPLY` — Mailgun config
5351

54-
- **Port**: `8080`
55-
- **Docker image**: `ghcr.io/constructive-io/constructive-functions/send-email-link:latest`
56-
- **Environment**:
57-
- `SEND_EMAIL_LINK_DRY_RUN` — If `true`, logs email payload without sending
58-
- `DEFAULT_DATABASE_ID` — Default database UUID
59-
- `GRAPHQL_URL` — GraphQL API endpoint
60-
- `META_GRAPHQL_URL` — Meta GraphQL API endpoint (optional)
61-
- `GRAPHQL_AUTH_TOKEN` — Optional Bearer token for GraphQL requests
62-
- `LOCAL_APP_PORT` — Local port for dashboard links (e.g., `3000`)
63-
- `MAILGUN_*` — Same Mailgun config as `simple-email`
52+
### `send-email-link`
6453

65-
## Development
54+
Sends invite, password reset, and verification emails (rendered via MJML).
6655

67-
This repo includes full Kubernetes manifests for deploying functions to any cluster.
56+
- `SEND_EMAIL_LINK_DRY_RUN` — if `true`, logs the payload instead of sending
57+
- `DEFAULT_DATABASE_ID` — default database UUID
58+
- `GRAPHQL_URL`, `META_GRAPHQL_URL` — GraphQL API endpoints
59+
- `GRAPHQL_AUTH_TOKEN` — optional Bearer token for GraphQL requests
60+
- `LOCAL_APP_PORT` — local port for dashboard links (e.g. `3000`)
61+
- `MAILGUN_*` — same Mailgun config as `simple-email`
6862

69-
### Local Kubernetes (kind/minikube)
63+
### `knative-job-example` / `python-example`
7064

71-
```bash
72-
cd k8s
65+
Reference implementations for the `node-graphql` and `python` templates.
7366

74-
# Install Knative Serving + Kourier only (no CNPG)
75-
make operators-knative-only
67+
## Development
7668

77-
# Apply local overlay (single Postgres, single MinIO, functions)
78-
make kustomize-local
69+
The full local-dev guide lives in [DEVELOPMENT.md](./DEVELOPMENT.md). Two paths:
7970

80-
# Port-forward to local services
81-
make proxy-server # GraphQL API -> localhost:8080
82-
make proxy-explorer # GraphQL explorer UI -> localhost:8081
83-
make proxy-web # Dashboard UI -> localhost:3000
84-
```
71+
- **Docker Compose + local Node** (fastest iteration) — `make dev` for infrastructure, `make dev-fn` to run functions as local Node processes.
72+
- **Skaffold on local k8s** (production-like, with hot reload) — `make skaffold-dev` deploys the full stack to the `constructive-functions` namespace and watches handler files.
8573

86-
See `k8s/DEVELOPMENT_LOCAL.md` for detailed local setup.
74+
For the Knative variant of the k8s setup, see `k8s/DEVELOPMENT_LOCAL.md`.
8775

8876
### CI/CD
8977

90-
The `CI Test K8s` workflow (`.github/workflows/test-k8s-deployment.yaml`) runs on every PR/push to `main` that touches `k8s/**`. It:
91-
92-
1. Spins up a `kind` cluster
93-
2. Installs Knative via `make operators-knative-only`
94-
3. Applies the `k8s/overlays/ci` overlay
95-
4. Waits for workloads to be ready
78+
The `CI Test K8s` workflow (`.github/workflows/test-k8s-deployment.yaml`) runs on PRs and pushes to `main` that touch `k8s/`, `tests/e2e/`, or `functions/`. It spins up a `kind` cluster, applies the `k8s/overlays/ci` overlay, and runs the per-function e2e tests.
9679

9780
## Project Structure
9881

9982
```
10083
.
101-
├── functions/
102-
│ ├── send-email-link/
103-
│ │ ├── src/
104-
│ │ ├── package.json
105-
│ │ ├── Dockerfile
106-
│ │ └── tsconfig.json
107-
│ └── simple-email/
108-
│ └── (same structure)
84+
├── functions/ # User-authored handler.ts + handler.json (git tracked)
85+
├── templates/ # Template definitions (node-graphql, python, shared, k8s)
86+
├── generated/ # Generated workspace packages (gitignored)
87+
├── packages/
88+
│ ├── fn-runtime/ # createFunctionServer, GraphQL clients, FunctionContext
89+
│ └── fn-app/ # Express app factory with job callbacks
90+
├── job/
91+
│ ├── service/ # Orchestrator (loads functions + worker + scheduler)
92+
│ ├── server/ # Callback receiver
93+
│ └── worker/ # Job dispatcher
10994
├── k8s/
110-
│ ├── base/
111-
│ ├── overlays/
112-
│ ├── scripts/
113-
│ └── Makefile
114-
├── types/
115-
│ └── (custom type definitions for external packages)
95+
│ ├── base/ # Shared manifests
96+
│ └── overlays/ # local-simple, local, ci, dev, staging
97+
├── tests/ # integration + e2e suites
98+
├── scripts/ # generate.ts, dev.ts, docker-build.ts
99+
├── skaffold.yaml
116100
├── package.json
117-
├── pnpm-workspace.yaml
118101
└── Makefile
119102
```
120103

121104
## Notes
122105

123-
- Functions use `@constructive-io/knative-job-fn` for the Express/Knative HTTP wrapper
124-
- Email providers use `@constructive-io/postmaster` (Mailgun) and `@launchql/mjml` (styled-email templates via MJML)
125-
- No `docker-compose` — this repo is Kubernetes-focused for functions deployment
126-
- Root workspace manages shared linting/formatting configs; each function has its own build config
106+
- Functions use `@constructive-io/fn-runtime` (runtime + GraphQL clients) and `@constructive-io/fn-app` (Express wrapper).
107+
- Email providers are wired through `@constructive-io/postmaster` (Mailgun) and `@launchql/mjml` / `@launchql/styled-email` for templating.
108+
- Both Node (`node-graphql`) and Python (`python`) templates are supported — pick via the `type` field in `handler.json`.
109+
- The `generated/` directory is entirely gitignored; rerun `pnpm generate` after changing any `handler.json`.
110+
- Root workspace manages shared linting/formatting; each generated function ships its own build config and Dockerfile.
127111

128112
## Pushing Images
129113

130-
To push to GitHub Container Registry:
114+
Images are tagged with the GHCR prefix automatically:
131115

132116
```bash
133117
docker push ghcr.io/constructive-io/constructive-functions/simple-email:latest
134118
docker push ghcr.io/constructive-io/constructive-functions/send-email-link:latest
119+
docker push ghcr.io/constructive-io/constructive-functions/knative-job-example:latest
120+
docker push ghcr.io/constructive-io/constructive-functions/python-example:latest
135121
```
136122

137-
Images are built with the correct registry prefix via the Makefile:
138-
- `make docker-build` — Builds all functions with `ghcr.io/constructive-io/constructive-functions/<name>:latest`
139-
- `make docker-build-simple-email` — Builds `ghcr.io/constructive-io/constructive-functions/simple-email:latest`
140-
- `make docker-build-send-email-link` — Builds `ghcr.io/constructive-io/constructive-functions/send-email-link:latest`
123+
- `make docker-build` — builds all function images
124+
- `make docker-build-<name>` — builds a single function image

docs/skills/adding-functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const handler: FunctionHandler<MyPayload> = async (params, context) => {
5959
// context provides: { client, meta, job, log, env }
6060
// client — GraphQL client for the database's API
6161
// meta — GraphQL client for metadata API
62-
// job — { jobId, workerId, databaseId }
62+
// job — { jobId, workerId, databaseId, actorId }
6363
// log — structured logger (info, error, warn, debug)
6464
// env — process.env
6565

0 commit comments

Comments
 (0)