@intentius/chant-lexicon-render 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/catalog.d.ts +100 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +21 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +6 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate-lexicon.d.ts +18 -0
- package/dist/codegen/generate-lexicon.d.ts.map +1 -0
- package/dist/codegen/generate-typescript.d.ts +12 -0
- package/dist/codegen/generate-typescript.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +15 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +14 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +17 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/render-deploy.d.ts +88 -0
- package/dist/composites/render-deploy.d.ts.map +1 -0
- package/dist/coverage.d.ts +15 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +60 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +12 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/generated/index.d.ts +38 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +5 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/integrity.json +18 -0
- package/dist/lint/audit-catalog.d.ts +13 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/post-synth/ren010-native-runtime-requires-commands.d.ts +12 -0
- package/dist/lint/post-synth/ren010-native-runtime-requires-commands.d.ts.map +1 -0
- package/dist/lint/post-synth/ren011-service-requires-source.d.ts +12 -0
- package/dist/lint/post-synth/ren011-service-requires-source.d.ts.map +1 -0
- package/dist/lint/post-synth/ren012-free-plan-cannot-scale.d.ts +11 -0
- package/dist/lint/post-synth/ren012-free-plan-cannot-scale.d.ts.map +1 -0
- package/dist/lint/post-synth/render-helpers.d.ts +19 -0
- package/dist/lint/post-synth/render-helpers.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +7 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/no-secret-literals.d.ts +13 -0
- package/dist/lint/rules/no-secret-literals.d.ts.map +1 -0
- package/dist/lint/rules/valid-cron-schedule.d.ts +12 -0
- package/dist/lint/rules/valid-cron-schedule.d.ts.map +1 -0
- package/dist/lint/rules/valid-region.d.ts +16 -0
- package/dist/lint/rules/valid-region.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +8 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +6 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/manifest.json +6 -0
- package/dist/meta.json +304 -0
- package/dist/okf/index.md +29 -0
- package/dist/okf/rules/REN001.md +11 -0
- package/dist/okf/rules/REN002.md +15 -0
- package/dist/okf/rules/REN003.md +15 -0
- package/dist/okf/rules/REN010.md +11 -0
- package/dist/okf/rules/REN011.md +11 -0
- package/dist/okf/rules/REN012.md +11 -0
- package/dist/okf/types/BackgroundWorker.md +24 -0
- package/dist/okf/types/CronJob.md +28 -0
- package/dist/okf/types/CustomDomain.md +14 -0
- package/dist/okf/types/Disk.md +16 -0
- package/dist/okf/types/EnvGroup.md +22 -0
- package/dist/okf/types/Environment.md +17 -0
- package/dist/okf/types/KeyValue.md +20 -0
- package/dist/okf/types/Postgres.md +29 -0
- package/dist/okf/types/PrivateService.md +24 -0
- package/dist/okf/types/Project.md +15 -0
- package/dist/okf/types/RegistryCredential.md +17 -0
- package/dist/okf/types/StaticSite.md +24 -0
- package/dist/okf/types/WebService.md +24 -0
- package/dist/okf/types/Webhook.md +17 -0
- package/dist/op/activities/fake-render.d.ts +52 -0
- package/dist/op/activities/fake-render.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +10 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/render-apply.d.ts +229 -0
- package/dist/op/activities/render-apply.d.ts.map +1 -0
- package/dist/ownership.d.ts +32 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/pseudo.d.ts +21 -0
- package/dist/pseudo.d.ts.map +1 -0
- package/dist/rules/no-secret-literals.ts +73 -0
- package/dist/rules/ren010-native-runtime-requires-commands.ts +47 -0
- package/dist/rules/ren011-service-requires-source.ts +50 -0
- package/dist/rules/ren012-free-plan-cannot-scale.ts +43 -0
- package/dist/rules/render-helpers.ts +38 -0
- package/dist/rules/valid-cron-schedule.ts +51 -0
- package/dist/rules/valid-region.ts +49 -0
- package/dist/serializer.d.ts +99 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/skills/chant-render-patterns.md +126 -0
- package/dist/skills/chant-render.md +112 -0
- package/dist/spec/fetch.d.ts +21 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +120 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/types/index.d.ts +573 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +15 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +73 -0
- package/src/catalog.ts +286 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +120 -0
- package/src/codegen/generate-cli.ts +36 -0
- package/src/codegen/generate-lexicon.ts +52 -0
- package/src/codegen/generate-typescript.ts +70 -0
- package/src/codegen/generate.ts +106 -0
- package/src/codegen/naming.ts +55 -0
- package/src/codegen/package.ts +30 -0
- package/src/composites/render-deploy.ts +133 -0
- package/src/coverage.ts +49 -0
- package/src/describe-resources.test.ts +143 -0
- package/src/describe-resources.ts +366 -0
- package/src/detect.ts +29 -0
- package/src/generated/index.d.ts +573 -0
- package/src/generated/index.ts +43 -0
- package/src/generated/lexicon-render.json +304 -0
- package/src/generated/runtime.ts +4 -0
- package/src/index.ts +30 -0
- package/src/lint/audit-catalog.ts +47 -0
- package/src/lint/post-synth/index.ts +11 -0
- package/src/lint/post-synth/post-synth.test.ts +151 -0
- package/src/lint/post-synth/ren010-native-runtime-requires-commands.ts +47 -0
- package/src/lint/post-synth/ren011-service-requires-source.ts +50 -0
- package/src/lint/post-synth/ren012-free-plan-cannot-scale.ts +43 -0
- package/src/lint/post-synth/render-helpers.ts +38 -0
- package/src/lint/rules/index.ts +11 -0
- package/src/lint/rules/no-secret-literals.ts +73 -0
- package/src/lint/rules/rules.test.ts +73 -0
- package/src/lint/rules/valid-cron-schedule.ts +51 -0
- package/src/lint/rules/valid-region.ts +49 -0
- package/src/lsp/completions.test.ts +28 -0
- package/src/lsp/completions.ts +22 -0
- package/src/lsp/hover.test.ts +32 -0
- package/src/lsp/hover.ts +50 -0
- package/src/op/activities/fake-render.ts +275 -0
- package/src/op/activities/index.ts +51 -0
- package/src/op/activities/render-apply.test.ts +403 -0
- package/src/op/activities/render-apply.ts +1042 -0
- package/src/ownership.ts +44 -0
- package/src/package-cli.ts +23 -0
- package/src/plugin.test.ts +54 -0
- package/src/plugin.ts +258 -0
- package/src/pseudo.ts +26 -0
- package/src/serializer.test.ts +221 -0
- package/src/serializer.ts +286 -0
- package/src/skills/chant-render-patterns.md +126 -0
- package/src/skills/chant-render.md +112 -0
- package/src/spec/fetch.ts +33 -0
- package/src/spec/parse.ts +721 -0
- package/src/validate-cli.ts +5 -0
- package/src/validate.ts +47 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-render-patterns
|
|
3
|
+
description: Render patterns in chant — env groups, projects and environments, disks, custom domains, image-backed and cron services, and the ownership model
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Render Patterns
|
|
8
|
+
|
|
9
|
+
Companion to `chant-render`. Each pattern is a fragment you can drop into `src/`.
|
|
10
|
+
|
|
11
|
+
## Env groups shared across services
|
|
12
|
+
|
|
13
|
+
An `EnvGroup` holds env vars (and secret files) many services share. Link services with `serviceIds`, passing the declared resources:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { EnvGroup, EnvVar, GeneratedEnvVar, WebService, BackgroundWorker, WebServiceDetails, BackgroundWorkerDetails } from "@intentius/chant-lexicon-render";
|
|
17
|
+
|
|
18
|
+
export const web = new WebService({ name: "web", repo: REPO, serviceDetails: new WebServiceDetails({ runtime: "docker" }) });
|
|
19
|
+
export const worker = new BackgroundWorker({ name: "worker", repo: REPO, serviceDetails: new BackgroundWorkerDetails({ runtime: "docker" }) });
|
|
20
|
+
|
|
21
|
+
export const shared = new EnvGroup({
|
|
22
|
+
name: "shared",
|
|
23
|
+
envVars: [
|
|
24
|
+
new EnvVar({ key: "LOG_LEVEL", value: "info" }),
|
|
25
|
+
new GeneratedEnvVar({ key: "SIGNING_KEY", generateValue: true }),
|
|
26
|
+
],
|
|
27
|
+
serviceIds: [web, worker],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The applier links each service after both exist (`POST /env-groups/{id}/services/{serviceId}`) and reconciles group vars per key on later runs. Env groups carry the ownership marker, so they are prunable.
|
|
32
|
+
|
|
33
|
+
## Projects and environments
|
|
34
|
+
|
|
35
|
+
A `Project` groups `Environment`s; a service, datastore, or env group joins one through `environmentId`. Reference the declared environment and the applier fills the id:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Project, ProjectEnvironment, Environment, Postgres } from "@intentius/chant-lexicon-render";
|
|
39
|
+
|
|
40
|
+
export const project = new Project({ name: "shop", environments: [new ProjectEnvironment({ name: "production" })] });
|
|
41
|
+
export const prod = new Environment({ name: "production", projectId: project, protectedStatus: "protected" });
|
|
42
|
+
export const db = new Postgres({ name: "shop-db", plan: "basic_1gb", version: "16", environmentId: prod });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`Project.environments` creates environments with the project (Render requires at least one); a separate `Environment` resource is how you reference one from other resources and PATCH it later. Reconciling by name means the environment created inline and the one declared separately are the same live object.
|
|
46
|
+
|
|
47
|
+
## Persistent disks
|
|
48
|
+
|
|
49
|
+
Two ways. Inline, as part of the service (created with it):
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
serviceDetails: new WebServiceDetails({ runtime: "docker", plan: "starter", disk: new ServiceDisk({ name: "data", mountPath: "/data", sizeGB: 10 }) })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or as a standalone `Disk` attached to a declared service, which can be resized independently:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
export const data = new Disk({ name: "data", sizeGB: 10, mountPath: "/data", serviceId: web });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Disks are only valid on web services, private services, and background workers (the `serviceId` type says so), and never on the free plan (REN012).
|
|
62
|
+
|
|
63
|
+
## Custom domains
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
export const apex = new CustomDomain({ name: "example.com", serviceId: web });
|
|
67
|
+
export const www = new CustomDomain({ name: "www.example.com", serviceId: web });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The domain is created under the service (`POST /services/{id}/custom-domains`); DNS verification stays a manual step in Render's dashboard or via the `/verify` endpoint. `describeResources` reports the domain's `verificationStatus` as its status.
|
|
71
|
+
|
|
72
|
+
## Image-backed services
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { WebService, WebServiceDetails, Image, RegistryCredential } from "@intentius/chant-lexicon-render";
|
|
76
|
+
|
|
77
|
+
export const cred = new RegistryCredential({ name: "ghcr", registry: "GITHUB", username: "me", authToken: process.env.GHCR_TOKEN! });
|
|
78
|
+
export const api = new WebService({
|
|
79
|
+
name: "api",
|
|
80
|
+
image: new Image({ imagePath: "ghcr.io/acme/api:1.4.2", registryCredentialId: cred }),
|
|
81
|
+
serviceDetails: new WebServiceDetails({ runtime: "image", plan: "starter" }),
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`Image.ownerId` is filled from the service's owner. `registryCredentialId` accepts the declared credential. Deploying a new tag is a change to `imagePath` → a PATCH → Render redeploys.
|
|
86
|
+
|
|
87
|
+
## Cron jobs
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export const nightly = new CronJob({
|
|
91
|
+
name: "nightly-report",
|
|
92
|
+
repo: REPO,
|
|
93
|
+
serviceDetails: new CronJobDetails({
|
|
94
|
+
runtime: "python",
|
|
95
|
+
schedule: "0 3 * * *",
|
|
96
|
+
envSpecificDetails: new NativeEnvironmentDetails({ buildCommand: "pip install -r requirements.txt", startCommand: "python report.py" }),
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`schedule` is a five-field cron expression (REN003); Render accepts no `@daily`-style macros.
|
|
102
|
+
|
|
103
|
+
## Static sites
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
export const site = new StaticSite({
|
|
107
|
+
name: "docs",
|
|
108
|
+
repo: REPO,
|
|
109
|
+
serviceDetails: new StaticSiteDetails({
|
|
110
|
+
buildCommand: "npm ci && npm run build",
|
|
111
|
+
publishPath: "dist",
|
|
112
|
+
routes: [new Route({ type: "rewrite", source: "/*", destination: "/index.html" })],
|
|
113
|
+
headers: [new Header({ path: "/*", name: "X-Frame-Options", value: "DENY" })],
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## The ownership model
|
|
119
|
+
|
|
120
|
+
Render has no tags or labels. chant's marker is an env var:
|
|
121
|
+
|
|
122
|
+
- **Services and env groups** carry `CHANT_MANAGED_BY=chant`, `CHANT_STACK=<stack>`, `CHANT_ENV=<env>` in their env vars. That is the primary marker: `describeResources` answers `owned` / `foreign` from it, and `prune: true` deletes only marked resources of the current stack that the plan no longer declares. A service someone created in the dashboard is `foreign` and is never modified or deleted by prune — but it *is* adopted by name if you declare it (a PATCH brings it to the declared shape and stamps the marker).
|
|
123
|
+
- **Disks and custom domains** have no marker but hang off a service and inherit its verdict — the service boundary. An undeclared disk or domain under a chant-owned service is pruned with `prune: true`; one under a foreign service is foreign and untouched.
|
|
124
|
+
- **Everything else** (Postgres, KeyValue, Project, Environment, RegistryCredential, Webhook) has no marker channel and no boundary; its verdict is `unknown` and it is never pruned. Remove one by dropping it from the plan and running `renderDelete` on the old plan, or by hand.
|
|
125
|
+
|
|
126
|
+
The marker keys are visible in the service's environment. That is deliberate — it is the same information a Kubernetes label or an AWS tag carries, in the only durable key/value store Render exposes.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-render
|
|
3
|
+
description: Author, lint, and deploy Render services, datastores, and env groups from a chant project, applied straight to the Render Public API
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Deploy to Render Operational Playbook
|
|
8
|
+
|
|
9
|
+
## How chant and Render relate
|
|
10
|
+
|
|
11
|
+
chant is a synthesis compiler: it compiles TypeScript in `src/` into a plan of Render Public API create requests, then reconciles that plan against a Render workspace. There is no external CLI to hand off to and no Blueprint (`render.yaml`) to keep in sync. `renderApply` speaks the Public API directly (`https://api.render.com/v1`), so the same code that builds the plan also applies it, and there is no state file to store, lock, or back up.
|
|
12
|
+
|
|
13
|
+
The source of truth is the TypeScript in `src/`. The serialized plan (a JSON object keyed by entity name, each value a `{ kind, entityType, endpoint, method, name, body }` request) is an intermediate artifact.
|
|
14
|
+
|
|
15
|
+
Your job as an agent:
|
|
16
|
+
|
|
17
|
+
- Use `chant build` for synthesis and lint (region, secret literals, cron schedule, runtime commands, source, free-plan scaling).
|
|
18
|
+
- Use `renderApply` (via the deploy Op, `chant run`) to reconcile the plan against the workspace: create or update each resource by name, wait each created service's deploy to `live`, and optionally prune what chant owns.
|
|
19
|
+
|
|
20
|
+
## Credentials and the workspace
|
|
21
|
+
|
|
22
|
+
Three environment variables:
|
|
23
|
+
|
|
24
|
+
- `RENDER_API_KEY` — a Render API key (Account Settings → API Keys). Required for every apply and read. Sent as `Authorization: Bearer`.
|
|
25
|
+
- `RENDER_OWNER_ID` — the workspace (team or personal) id, `tea-…` or `usr-…`. Every `ownerId` chant fills in defaults to it (`Render.OwnerId`). If unset, the applier reads `GET /owners` and uses the sole workspace the key can see; several visible workspaces is an error, never a guess.
|
|
26
|
+
- `RENDER_API_BASE_URL` — optional endpoint override (a local stand-in). Default `https://api.render.com/v1`.
|
|
27
|
+
|
|
28
|
+
Resolution order for the endpoint is: an explicit `endpoint` arg, then `RENDER_API_BASE_URL`, then the default.
|
|
29
|
+
|
|
30
|
+
Start from the runnable [`examples/getting-started`](../../lexicons/render/examples/getting-started) stack:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd lexicons/render/examples/getting-started
|
|
34
|
+
export RENDER_API_KEY=rnd_… RENDER_OWNER_ID=tea-…
|
|
35
|
+
npm run build # chant build src --lexicon render -o dist/render.json
|
|
36
|
+
chant run render # build → renderApply (creates the web service, waits for its deploy)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Author a service and a database
|
|
40
|
+
|
|
41
|
+
Import resource types from `@intentius/chant-lexicon-render`. They are generated from Render's Public API OpenAPI spec, so `WebServiceDetails`, `Image`, `ServiceDisk`, and friends are typed all the way down, and every enum (`plan`, `region`, `runtime`) is a string-literal union.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import {
|
|
45
|
+
WebService, WebServiceDetails, NativeEnvironmentDetails, EnvVar, GeneratedEnvVar, Postgres, Render,
|
|
46
|
+
} from "@intentius/chant-lexicon-render";
|
|
47
|
+
|
|
48
|
+
export const db = new Postgres({ name: "app-db", plan: "free", version: "16", region: Render.Region });
|
|
49
|
+
|
|
50
|
+
export const web = new WebService({
|
|
51
|
+
name: "app-web",
|
|
52
|
+
repo: "https://github.com/render-examples/express-hello-world",
|
|
53
|
+
branch: "main",
|
|
54
|
+
serviceDetails: new WebServiceDetails({
|
|
55
|
+
runtime: "node",
|
|
56
|
+
plan: "starter",
|
|
57
|
+
region: Render.Region,
|
|
58
|
+
envSpecificDetails: new NativeEnvironmentDetails({ buildCommand: "npm ci", startCommand: "npm start" }),
|
|
59
|
+
}),
|
|
60
|
+
envVars: [
|
|
61
|
+
new EnvVar({ key: "DATABASE_URL", value: db.internalConnectionString }),
|
|
62
|
+
new GeneratedEnvVar({ key: "SESSION_SECRET", generateValue: true }),
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Each service type is its own class — `WebService`, `StaticSite`, `PrivateService`, `BackgroundWorker`, `CronJob` — with `serviceDetails` narrowed to that type's shape (`CronJobDetails` requires `schedule`; `StaticSiteDetails` has `publishPath` and `routes`). The other resources are `Postgres`, `KeyValue`, `EnvGroup`, `Project`, `Environment`, `Disk`, `CustomDomain`, `RegistryCredential`, and `Webhook`.
|
|
68
|
+
|
|
69
|
+
You do not stamp the ownership marker yourself: the serializer writes `CHANT_MANAGED_BY=chant` (plus `CHANT_STACK` / `CHANT_ENV`) into each service's and env group's `envVars`, and the owned-only prune reads it back.
|
|
70
|
+
|
|
71
|
+
### References between resources
|
|
72
|
+
|
|
73
|
+
Render assigns ids on create, so a Disk's `serviceId`, an Environment's `projectId`, or a service's `environmentId` cannot be a literal at build time. Pass the declared resource instead — `serviceId: web`, `projectId: project`, `environmentId: env` — and the applier substitutes the live id after the target exists. Attribute reads work the same way: `db.internalConnectionString`, `db.externalConnectionString`, `kv.internalConnectionString`, `web.id`, `web.dashboardUrl` resolve from the live resource (connection strings from the `/connection-info` endpoint).
|
|
74
|
+
|
|
75
|
+
## Build and lint
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
chant build src/
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Build synthesizes the plan and runs the lint rules before anything reaches the API:
|
|
82
|
+
|
|
83
|
+
| Rule | Catches |
|
|
84
|
+
|------|---------|
|
|
85
|
+
| REN001 | `region` is not a Render region (frankfurt, oregon, ohio, singapore, virginia) |
|
|
86
|
+
| REN002 | A secret-looking env var (`*_PASSWORD`, `*_TOKEN`, `API_KEY`, …) with a literal value |
|
|
87
|
+
| REN003 | A `schedule` that is not a five-field cron expression |
|
|
88
|
+
| REN010 | A native-runtime service (node, python, …) with no `buildCommand`/`startCommand` |
|
|
89
|
+
| REN011 | A service with neither `repo` nor `image`, or `runtime: "image"` without an image |
|
|
90
|
+
| REN012 | A free-plan service with `numInstances > 1`, `autoscaling`, or a `disk` |
|
|
91
|
+
|
|
92
|
+
Fix every reported violation before applying. Secret values belong in `generateValue: true`, an `EnvGroup`, a resource attribute, or `process.env` — never a literal (REN002).
|
|
93
|
+
|
|
94
|
+
## Apply with renderApply
|
|
95
|
+
|
|
96
|
+
`renderApply` reads the plan and, in dependency order (projects → environments → env groups/datastores → services → disks/domains → webhooks):
|
|
97
|
+
|
|
98
|
+
1. Resolves `{ $ref }`, `{ $attr }`, and `{ $owner }` markers from what is already live.
|
|
99
|
+
2. Finds the existing resource by name (services also by type; environments by project; disks by service).
|
|
100
|
+
3. `POST`s a create when absent, or `PATCH`es the differing patchable fields when present. Service env vars are replaced through `PUT /services/{id}/env-vars`; a `generateValue` var keeps its live value rather than being regenerated. Env-group vars are reconciled per key.
|
|
101
|
+
4. Waits each **created** service's first deploy to `live` (`wait.deploys: false` to skip; `wait.deadlineMs` default 15 minutes) and throws on `build_failed`/`update_failed`/`canceled`.
|
|
102
|
+
5. With `prune: true`, deletes services and env groups that carry the marker for this stack but are no longer in the plan, and the disks and custom domains under an owned declared service that the plan no longer declares. Foreign resources (no marker) and other stacks' resources are never touched. Datastores, projects, environments, registry credentials, and webhooks are never pruned — they have no marker; remove them explicitly with `renderDelete`.
|
|
103
|
+
|
|
104
|
+
Returns the versioned apply envelope: `applied` (`created` / `updated` / `unchanged`, with the Render id as `physicalId`) and `pruned`.
|
|
105
|
+
|
|
106
|
+
## Read back with plan
|
|
107
|
+
|
|
108
|
+
`chant lifecycle plan` (via `describeResources`) lists what is live for every declared entity, with an ownership verdict: services and env groups are `owned` or `foreign` by the marker; disks and custom domains inherit their service's verdict; everything else is `unknown`, which the change set never escalates to a delete. Undeclared chant-owned services, env groups, disks, and domains surface as owned orphans, so a removed declaration shows up as a delete candidate before you prune.
|
|
109
|
+
|
|
110
|
+
## Teardown
|
|
111
|
+
|
|
112
|
+
`renderDelete` (or `renderDeploy({ teardown: true })`) deletes what the plan names in reverse order, looking each up by name; already-gone resources are reported `deleted: false`. Only what the plan declares is touched.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render's published Public API OpenAPI 3.0 spec.
|
|
3
|
+
*
|
|
4
|
+
* Render serves the current spec at a fixed URL with no version or release tag
|
|
5
|
+
* to pin (the document's own `info.version` has sat at "1.0.0" across many
|
|
6
|
+
* additive changes), so — like fly — the plugin declares no `upstreamPin`.
|
|
7
|
+
* Re-running `generate` picks up whatever the URL currently serves.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCHEMA_URL = "https://api-docs.render.com/v1.0/openapi/render-public-api-1.json";
|
|
10
|
+
/**
|
|
11
|
+
* Fetch the Render OpenAPI spec and return it as a Map<typeName, Buffer>
|
|
12
|
+
* compatible with the generatePipeline fetchSchemas callback.
|
|
13
|
+
*
|
|
14
|
+
* The spec is a single document, so we return a single entry keyed by
|
|
15
|
+
* "Render::OpenAPI" — the parse step splits it into the curated resources and
|
|
16
|
+
* their reachable property types.
|
|
17
|
+
*/
|
|
18
|
+
export declare function fetchSchemas(options?: {
|
|
19
|
+
force?: boolean;
|
|
20
|
+
}): Promise<Map<string, Buffer>>;
|
|
21
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/spec/fetch.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,sEAAsE,CAAC;AAS9F;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAG9F"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render Public API OpenAPI 3.0 parser.
|
|
3
|
+
*
|
|
4
|
+
* Render's spec is a REST API description — request/response DTOs, not a
|
|
5
|
+
* resource catalog — so, like fly, we generate a *curated* set of resources
|
|
6
|
+
* rather than every schema. Each curated resource pairs a create-request
|
|
7
|
+
* schema (the writable authoring surface) with a response schema (read-only
|
|
8
|
+
* attributes). The named object schemas reachable from the request schemas
|
|
9
|
+
* (`webServiceDetailsPOST`, `image`, `serviceDisk`, `cidrBlockAndDescription`,
|
|
10
|
+
* ...) become standalone property-type classes so nested config is typed all
|
|
11
|
+
* the way down; anonymous inline objects (Render leans on `#/paths/...` JSON
|
|
12
|
+
* pointers for a few of these — autoscaling, env vars, secret files) are
|
|
13
|
+
* emitted as inline TypeScript object literals, and enums inline as
|
|
14
|
+
* string-literal unions.
|
|
15
|
+
*
|
|
16
|
+
* Services are split by type. Render's `POST /services` takes a `type`
|
|
17
|
+
* discriminator plus a `serviceDetails` oneOf; authoring `new WebService({...})`
|
|
18
|
+
* with `serviceDetails` typed as `WebServiceDetails` is both tighter and more
|
|
19
|
+
* honest than one `Service` class with a five-way union, so each service type
|
|
20
|
+
* is its own resource with `type` fixed and `serviceDetails` narrowed.
|
|
21
|
+
*/
|
|
22
|
+
import { type PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
23
|
+
export type { PropertyConstraints };
|
|
24
|
+
export interface ParsedProperty {
|
|
25
|
+
name: string;
|
|
26
|
+
tsType: string;
|
|
27
|
+
required: boolean;
|
|
28
|
+
description?: string;
|
|
29
|
+
constraints: PropertyConstraints;
|
|
30
|
+
}
|
|
31
|
+
export interface ParsedAttribute {
|
|
32
|
+
name: string;
|
|
33
|
+
tsType: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ParsedResource {
|
|
36
|
+
typeName: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
properties: ParsedProperty[];
|
|
39
|
+
attributes: ParsedAttribute[];
|
|
40
|
+
}
|
|
41
|
+
export interface RenderParseResult {
|
|
42
|
+
resource: ParsedResource;
|
|
43
|
+
/** Always empty — render emits property types as standalone results. */
|
|
44
|
+
propertyTypes: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
defType: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Always empty — enums are inlined as string-literal unions. */
|
|
49
|
+
enums: Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
values: string[];
|
|
52
|
+
}>;
|
|
53
|
+
/** Whether this entity is a property type (nested config shape). */
|
|
54
|
+
isProperty?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** The type-name namespace every render entity lives under. */
|
|
57
|
+
export declare const NAMESPACE = "Render";
|
|
58
|
+
/**
|
|
59
|
+
* How a curated resource maps onto the spec. `request`/`response` are schema
|
|
60
|
+
* locators: either a `#/components/schemas/X` name (bare `X`) or a JSON
|
|
61
|
+
* pointer into `#/paths/...` for the handful of bodies Render leaves inline.
|
|
62
|
+
*/
|
|
63
|
+
export interface ResourceSpec {
|
|
64
|
+
typeName: string;
|
|
65
|
+
request: string;
|
|
66
|
+
response: string;
|
|
67
|
+
/**
|
|
68
|
+
* Request properties chant fixes and therefore hides from the authoring
|
|
69
|
+
* surface — the service `type` discriminator. The serializer re-injects them.
|
|
70
|
+
*/
|
|
71
|
+
fixed?: Record<string, unknown>;
|
|
72
|
+
/** Property → schema locator that replaces the request schema's own type (narrowing `serviceDetails`). */
|
|
73
|
+
overrides?: Record<string, string>;
|
|
74
|
+
/**
|
|
75
|
+
* Properties the API requires but chant can default — `ownerId` resolves from
|
|
76
|
+
* the `Render.OwnerId` pseudo-parameter (`RENDER_OWNER_ID`) at build time.
|
|
77
|
+
*/
|
|
78
|
+
optional?: string[];
|
|
79
|
+
/** Synthetic properties not in the request schema (path parameters such as CustomDomain's `serviceId`). */
|
|
80
|
+
extraProperties?: ParsedProperty[];
|
|
81
|
+
/** Synthetic attributes not in the response schema (datastore connection strings, read from a side endpoint). */
|
|
82
|
+
extraAttributes?: ParsedAttribute[];
|
|
83
|
+
/**
|
|
84
|
+
* Property → additional TS union members. Render's cross-resource fields are
|
|
85
|
+
* opaque ids (`serviceId`, `projectId`, `environmentId`); widening them to
|
|
86
|
+
* accept the declared resource lets `serviceId: web` reference another entity
|
|
87
|
+
* and have the applier substitute the live id after it exists.
|
|
88
|
+
*/
|
|
89
|
+
widen?: Record<string, string>;
|
|
90
|
+
/** Property → full TS type replacement (array-valued references, where a plain union would not typecheck). */
|
|
91
|
+
retype?: Record<string, string>;
|
|
92
|
+
/**
|
|
93
|
+
* Properties the API marks optional but that are required in practice —
|
|
94
|
+
* `serviceDetails` on every runtime-bearing service (its `runtime` is
|
|
95
|
+
* required; a WebService without it is rejected by the API).
|
|
96
|
+
*/
|
|
97
|
+
require?: string[];
|
|
98
|
+
}
|
|
99
|
+
export declare const RESOURCES: ResourceSpec[];
|
|
100
|
+
/**
|
|
101
|
+
* Parse the Render OpenAPI spec into the curated resources and the property
|
|
102
|
+
* types reachable from their request schemas.
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseRenderOpenAPI(data: string | Buffer): RenderParseResult[];
|
|
105
|
+
/** Apply the schema aliases (spec quirks) to a component name. */
|
|
106
|
+
export declare function canonicalSchemaName(name: string): string;
|
|
107
|
+
/**
|
|
108
|
+
* Convert a component schema name into a PascalCase class name, dropping the
|
|
109
|
+
* request-shape suffixes Render uses so the authoring surface reads as the
|
|
110
|
+
* concept, not the wire DTO:
|
|
111
|
+
* "webServiceDetailsPOST" → "WebServiceDetails", "readReplicaInput" →
|
|
112
|
+
* "ReadReplica", "routePost" → "Route", "cidrBlockAndDescription" →
|
|
113
|
+
* "CidrBlockAndDescription".
|
|
114
|
+
*/
|
|
115
|
+
export declare function schemaToClassName(schemaName: string): string;
|
|
116
|
+
/** Extract short name: "Render::Services::WebService" → "WebService". */
|
|
117
|
+
export declare function renderShortName(typeName: string): string;
|
|
118
|
+
/** Extract service name: "Render::Services::WebService" → "Services". */
|
|
119
|
+
export declare function renderServiceName(typeName: string): string;
|
|
120
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,sCAAsC,CAAC;AAI9C,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,CAAC;IACzB,wEAAwE;IACxE,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,iEAAiE;IACjE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACjD,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAqBD,+DAA+D;AAC/D,eAAO,MAAM,SAAS,WAAW,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,2GAA2G;IAC3G,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,iHAAiH;IACjH,eAAe,CAAC,EAAE,eAAe,EAAE,CAAC;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,8GAA8G;IAC9G,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAgDD,eAAO,MAAM,SAAS,EAAE,YAAY,EA0EnC,CAAC;AA+DF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,iBAAiB,EAAE,CAuF7E;AA2RD,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAID;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D"}
|