@sanoma-ai/workflows 0.11.3 → 0.11.5

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.
Files changed (61) hide show
  1. package/KMS-SECRETS.md +3 -1
  2. package/README.md +33 -31
  3. package/dist/apply-steps.d.ts +4 -2
  4. package/dist/apply-steps.js +5 -3
  5. package/dist/apply-steps.js.map +1 -1
  6. package/dist/apply.d.ts +6 -1
  7. package/dist/apply.js +55 -20
  8. package/dist/apply.js.map +1 -1
  9. package/dist/config.d.ts +4 -0
  10. package/dist/config.js +52 -5
  11. package/dist/config.js.map +1 -1
  12. package/dist/constructors.d.ts +1 -1
  13. package/dist/definitions.d.ts +5 -0
  14. package/dist/definitions.js.map +1 -1
  15. package/dist/describe.d.ts +1 -1
  16. package/dist/describe.js +16 -11
  17. package/dist/describe.js.map +1 -1
  18. package/dist/drift.d.ts +7 -0
  19. package/dist/drift.js +15 -3
  20. package/dist/drift.js.map +1 -1
  21. package/dist/host-derive.d.ts +145 -0
  22. package/dist/host-derive.js +403 -0
  23. package/dist/host-derive.js.map +1 -0
  24. package/dist/host.d.ts +70 -5
  25. package/dist/host.js +56 -1
  26. package/dist/host.js.map +1 -1
  27. package/dist/index.d.ts +5 -4
  28. package/dist/index.js +5 -4
  29. package/dist/index.js.map +1 -1
  30. package/dist/kms-secrets.js +24 -4
  31. package/dist/kms-secrets.js.map +1 -1
  32. package/dist/resource.d.ts +10 -2
  33. package/dist/resource.js +19 -4
  34. package/dist/resource.js.map +1 -1
  35. package/dist/resources.js +1 -1
  36. package/dist/resources.js.map +1 -1
  37. package/dist/sites.d.ts +6 -0
  38. package/dist/sites.js +28 -4
  39. package/dist/sites.js.map +1 -1
  40. package/dist/source.d.ts +12 -5
  41. package/dist/source.js +32 -13
  42. package/dist/source.js.map +1 -1
  43. package/dist/wakeup-type.d.ts +1 -1
  44. package/dist/wakeup-type.js +1 -1
  45. package/dist/wakeup-type.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/apply-steps.ts +5 -4
  48. package/src/apply.ts +87 -24
  49. package/src/config.ts +59 -5
  50. package/src/definitions.ts +5 -0
  51. package/src/describe.ts +23 -10
  52. package/src/drift.ts +16 -3
  53. package/src/host-derive.ts +492 -0
  54. package/src/host.ts +101 -5
  55. package/src/index.ts +20 -2
  56. package/src/kms-secrets.ts +34 -5
  57. package/src/resource.ts +45 -7
  58. package/src/resources.ts +1 -1
  59. package/src/sites.ts +33 -3
  60. package/src/source.ts +35 -12
  61. package/src/wakeup-type.ts +1 -1
@@ -0,0 +1,145 @@
1
+ import type { Driver } from "./op.ts";
2
+ import type { DeclaredResource, ResourceProblem } from "./description.ts";
3
+ import type { Bindings } from "./lockfile.ts";
4
+ import { type Resource } from "./resource.ts";
5
+ import { type Secrets } from "./secret.ts";
6
+ /** The private half of the key a host clones a private GitHub repository with: minted at its first apply, held in the store. */
7
+ export declare const SOURCE_KEY = "SANOMA_SOURCE_KEY";
8
+ /**
9
+ * Where the company's GitHub App's keys are stored, by what GitHub's manifest conversion calls
10
+ * them: the app's Connect page writes them, the staff login reads the client's, and a host carries
11
+ * the ones that are stored. The one list of them (a connection of its own is later).
12
+ */
13
+ export declare const GITHUB_APP: {
14
+ readonly id: "SANOMA_GITHUB_APP_ID";
15
+ readonly slug: "SANOMA_GITHUB_APP_SLUG";
16
+ readonly client_id: "SANOMA_GITHUB_CLIENT_ID";
17
+ readonly client_secret: "SANOMA_GITHUB_CLIENT_SECRET";
18
+ readonly webhook_secret: "SANOMA_GITHUB_WEBHOOK_SECRET";
19
+ readonly pem: "SANOMA_GITHUB_APP_PEM";
20
+ };
21
+ /** The signing key a host is given when the config binds none, generated at its create and kept in the store. */
22
+ export declare const HOST_SIGNING_KEY = "SANOMA_HOST_SIGNING_KEY";
23
+ /** The type of the key that lets a host clone its GitHub repository, which apply declares on it. */
24
+ export declare const DEPLOY_KEY_TYPE = "github.repository_deploy_key";
25
+ /** The title a host's deploy key has on GitHub. */
26
+ export declare const DEPLOY_KEY_TITLE = "Sanoma host";
27
+ /** What the runtime knows that a host derives from, read once per apply or drift run (`hostFacts`). */
28
+ export interface HostFacts {
29
+ /** The `sanoma` version the repository installs: the runtime image's tag. Undefined when it is not installed. */
30
+ version?: string;
31
+ /** The clone URL of the repository the app runs from (`origin`), with no credentials. */
32
+ origin?: string;
33
+ /** Each public site's domain, by site name (`siteDomains`). */
34
+ domains: Readonly<Record<string, string>>;
35
+ /** True when the config keeps its secrets in the built-in store, which `SANOMA_SECRETS_KEY` unlocks. */
36
+ builtInStore: boolean;
37
+ /**
38
+ * The secrets a host carries for each vendor it deploys itself with (its data file's) or signs
39
+ * people in through (the login's sender), by vendor: the variables the vendor's driver reads
40
+ * that the store holds (one only in this machine's environment is not given to the host).
41
+ */
42
+ carried: ReadonlyMap<string, readonly string[]>;
43
+ /** The login sender's vendor, whose credentials a host carries so it sends sign-in codes. */
44
+ sender?: string;
45
+ /** The company's GitHub App's keys that are stored (`GITHUB_APP`), which a host carries for sign-in and webhooks. */
46
+ githubApp: readonly string[];
47
+ /** The public half of the stored `SANOMA_SOURCE_KEY`, when one is: what apply declares on the repository. */
48
+ sourceKey?: string;
49
+ /**
50
+ * By vendor, the variables its driver reads that are set in this machine's environment but
51
+ * neither bound nor stored: the driver runs here, and a host would not be given them.
52
+ */
53
+ unbound?: ReadonlyMap<string, readonly string[]>;
54
+ /** True when the config binds `SANOMA_SIGNING_KEY`: the host is given that key, else its own (`HOST_SIGNING_KEY`). */
55
+ signingKeyBound?: boolean;
56
+ /**
57
+ * While the config has no deploy-key type, the GitHub repositories a host may clone (`origin`'s,
58
+ * a declared `source`'s) that GitHub shows to no one: private (or not there). One GitHub does
59
+ * not answer for is not among them.
60
+ */
61
+ privateRepositories?: readonly string[];
62
+ }
63
+ /** The version of `sanoma` the repository at `root` installs (a hoisted install counts); undefined when none does. */
64
+ export declare function installedSanoma(root: string): Promise<string | undefined>;
65
+ /**
66
+ * A clone URL as a host takes it: GitHub's SSH forms (`git@github.com:o/n.git`,
67
+ * `ssh://git@github.com/o/n`) as `https://github.com/o/n.git`, and any credentials an https URL
68
+ * carries left out (an `origin` may hold a token). Undefined for anything that is not a URL.
69
+ */
70
+ export declare function cloneUrl(url: string): string | undefined;
71
+ /** The URL of `origin` in the git repository at `root`, as a host clones it (`cloneUrl`); undefined when it has none. */
72
+ export declare function originUrl(root: string): Promise<string | undefined>;
73
+ /** The GitHub repository a clone URL names, `owner/name`; undefined for one of another host. */
74
+ export declare function githubRepository(url: unknown): string | undefined;
75
+ /**
76
+ * The public half of the source key in `secrets`; with `mint`, a new pair made first when none is
77
+ * stored (its private half stored, never returned), as `sanoma.secret.generate`'s `ssh-rsa` does.
78
+ * Undefined when none is stored and none is minted, or the stored value is no RSA key.
79
+ */
80
+ export declare function sourceKeyOf(secrets: Secrets, mint: boolean): Promise<string | undefined>;
81
+ /** Mints the instance token a host's wakeup checks it by, once, when none is stored. */
82
+ export declare const mintWakeupToken: (secrets: Secrets) => Promise<string>;
83
+ /**
84
+ * The declared repository a host that leaves out its `source` runs from: of the types its
85
+ * `source` reference names, the one `origin` is, else the one the host's own data file declares.
86
+ */
87
+ export declare function sourceRepository(host: DeclaredResource, type: Resource, resources: readonly DeclaredResource[], origin: string | undefined): DeclaredResource | undefined;
88
+ /** The declared wakeup that names `host` (its `inputs.host` refers to it), if any. */
89
+ export declare function wokenBy(host: Pick<DeclaredResource, "id">, resources: readonly DeclaredResource[], types: ReadonlyMap<string, Pick<Resource, "wakeup">>): DeclaredResource | undefined;
90
+ /**
91
+ * Fills, in place, each declared host that derives (`HostSpec.derives`) with what it is not told,
92
+ * and puts the deploy key it clones with before it in `resources`:
93
+ *
94
+ * - `name`, left out (when its type takes `inputs.name`): the name it is bound by, else its
95
+ * repository's (`nameOf`), unless another host of its type has that name;
96
+ * - `image`, left out: the runtime image of the installed `sanoma` (`runtimeImage`);
97
+ * - `source`, left out: the one repository its own data file declares (a reference to it, of a
98
+ * type its `source` names), else the repository the app runs from (`origin`);
99
+ * - `domains`: each public site's domain (`siteDomains`);
100
+ * - `secrets` (unless it binds its own bootstrap), markers only: the declared `database`'s URL as
101
+ * `SANOMA_DATABASE_URL`, the runtime's keys (the signing key the config binds, else the host's
102
+ * own, generated at its create; the encryption key with the built-in store), the source key for
103
+ * a GitHub repository it can declare a deploy key on, the wakeup's token (and URL, for
104
+ * `sanoma.wakeup`) when a wakeup names it, the credentials of its own vendor, its repository's
105
+ * and the login sender's, and the company's GitHub App's keys that are stored;
106
+ * - for a GitHub repository, a `github.repository_deploy_key` (`sourceKeyId`), read-only, whose
107
+ * key is the stored source key's public half, when one is and the config has the type; found by
108
+ * its type's identity (the repository and the key), so a key GitHub holds already is bound, not
109
+ * made again.
110
+ *
111
+ * `ref` is resolved by apply (and drift) with the revision: the repository's default branch.
112
+ * `out.keep` takes the deploy-key ids not declared this read (no source key stored yet, or its
113
+ * host left out), which the caller must not orphan; `out.leftOut` the hosts with a problem, which
114
+ * the caller leaves out, with what names them, so apply changes nothing it cannot give all it needs.
115
+ * Problems: a host with no `database`, a name (none to tell, or another host's), an image or a
116
+ * source that cannot be told, a credential its vendor's driver reads from this machine's
117
+ * environment that nothing binds, a private repository it has no deploy key for, two hosts that
118
+ * would attach the sites' domains.
119
+ */
120
+ export declare function deriveHosts(resources: DeclaredResource[], types: ReadonlyMap<string, Resource>, bindings: Bindings, facts: HostFacts, out?: {
121
+ keep?: Set<string>;
122
+ leftOut?: Set<string>;
123
+ }): ResourceProblem[];
124
+ /**
125
+ * What the declared hosts that derive (`deriveHosts`) are filled from, read now: the installed
126
+ * `sanoma`, `origin`, the public sites' domains, and of `secrets` (none outside a run) which
127
+ * optional credentials, App keys and source key are stored, and which credentials the drivers
128
+ * read from this machine's environment alone (`unbound`). With `mint` (apply, never drift or a
129
+ * sandbox run), the source key (where the config has the deploy-key type) and, when a wakeup is
130
+ * declared, the wakeup's token are minted first when none is stored, so the host is given them
131
+ * from its first apply. With no deploy-key type, GitHub is asked which repositories a host may clone are private.
132
+ * Undefined when no declared host derives.
133
+ */
134
+ export declare function hostFacts(options: {
135
+ root: string;
136
+ resources: readonly DeclaredResource[];
137
+ types: ReadonlyMap<string, Resource>;
138
+ drivers: ReadonlyMap<string, Driver>;
139
+ secrets?: Secrets;
140
+ builtInStore: boolean;
141
+ /** True when the config binds `SANOMA_SIGNING_KEY` (`{ env }` or `{ bootstrap }`). */
142
+ signingKeyBound?: boolean;
143
+ sender?: string;
144
+ mint: boolean;
145
+ }): Promise<HostFacts | undefined>;
@@ -0,0 +1,403 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readFile } from "node:fs/promises";
3
+ import { findPackageJSON } from "node:module";
4
+ import { join } from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { databaseUrlSecret } from "./database.js";
7
+ import { statusesOf } from "./env.js";
8
+ import { runtimeImage, sourceKeyId } from "./host.js";
9
+ import { valueAt } from "./resource.js";
10
+ import { secret } from "./secret.js";
11
+ import { sourceKeyPair, sshPublicKey } from "./ssh-key.js";
12
+ import { mintInstanceToken, WAKEUP_TOKEN } from "./wakeup-service.js";
13
+ // What a host that derives (`HostSpec.derives`) is given beyond its decisions: a data file says
14
+ // where it runs, its name, its hostname and the database it runs on, and apply fills the rest
15
+ // before it plans, as it fills a host's revision. Nothing here is ever a secret's value: the
16
+ // secrets a host is given are markers (`secret("NAME")`), resolved by its driver just before the
17
+ // vendor call, so a declaration, a plan, a ledger entry or a page holds names only.
18
+ /** The private half of the key a host clones a private GitHub repository with: minted at its first apply, held in the store. */
19
+ export const SOURCE_KEY = "SANOMA_SOURCE_KEY";
20
+ /**
21
+ * Where the company's GitHub App's keys are stored, by what GitHub's manifest conversion calls
22
+ * them: the app's Connect page writes them, the staff login reads the client's, and a host carries
23
+ * the ones that are stored. The one list of them (a connection of its own is later).
24
+ */
25
+ export const GITHUB_APP = {
26
+ id: "SANOMA_GITHUB_APP_ID",
27
+ slug: "SANOMA_GITHUB_APP_SLUG",
28
+ client_id: "SANOMA_GITHUB_CLIENT_ID",
29
+ client_secret: "SANOMA_GITHUB_CLIENT_SECRET",
30
+ webhook_secret: "SANOMA_GITHUB_WEBHOOK_SECRET",
31
+ pem: "SANOMA_GITHUB_APP_PEM",
32
+ };
33
+ /** The signing key a host is given when the config binds none, generated at its create and kept in the store. */
34
+ export const HOST_SIGNING_KEY = "SANOMA_HOST_SIGNING_KEY";
35
+ /** The wakeup service's URL, which a host is given beside its token when `sanoma.wakeup` wakes it. */
36
+ const WAKEUP_URL = "SANOMA_WAKEUP_URL";
37
+ /** The type of the key that lets a host clone its GitHub repository, which apply declares on it. */
38
+ export const DEPLOY_KEY_TYPE = "github.repository_deploy_key";
39
+ /** The title a host's deploy key has on GitHub. */
40
+ export const DEPLOY_KEY_TITLE = "Sanoma host";
41
+ const run = promisify(execFile);
42
+ /** The version of `sanoma` the repository at `root` installs (a hoisted install counts); undefined when none does. */
43
+ export async function installedSanoma(root) {
44
+ try {
45
+ const at = findPackageJSON("sanoma", join(root, "package.json"));
46
+ const version = at && JSON.parse(await readFile(at, "utf8")).version;
47
+ return typeof version === "string" ? version : undefined;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ /**
54
+ * A clone URL as a host takes it: GitHub's SSH forms (`git@github.com:o/n.git`,
55
+ * `ssh://git@github.com/o/n`) as `https://github.com/o/n.git`, and any credentials an https URL
56
+ * carries left out (an `origin` may hold a token). Undefined for anything that is not a URL.
57
+ */
58
+ export function cloneUrl(url) {
59
+ const ssh = /^(?:ssh:\/\/)?git@github\.com[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(url.trim());
60
+ if (ssh)
61
+ return `https://github.com/${ssh[1]}/${ssh[2]}.git`;
62
+ if (!URL.canParse(url))
63
+ return undefined;
64
+ const parsed = new URL(url);
65
+ parsed.username = "";
66
+ parsed.password = "";
67
+ if (parsed.hostname === "github.com" && !parsed.pathname.endsWith(".git"))
68
+ parsed.pathname += ".git";
69
+ return parsed.href;
70
+ }
71
+ /** The URL of `origin` in the git repository at `root`, as a host clones it (`cloneUrl`); undefined when it has none. */
72
+ export async function originUrl(root) {
73
+ try {
74
+ const { stdout } = await run("git", ["-C", root, "remote", "get-url", "origin"], { timeout: 10_000 });
75
+ return cloneUrl(stdout.trim());
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ }
81
+ /** The GitHub repository a clone URL names, `owner/name`; undefined for one of another host. */
82
+ export function githubRepository(url) {
83
+ if (typeof url !== "string")
84
+ return undefined;
85
+ const at = /^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(cloneUrl(url) ?? "");
86
+ return at ? `${at[1]}/${at[2]}` : undefined;
87
+ }
88
+ /**
89
+ * The public half of the source key in `secrets`; with `mint`, a new pair made first when none is
90
+ * stored (its private half stored, never returned), as `sanoma.secret.generate`'s `ssh-rsa` does.
91
+ * Undefined when none is stored and none is minted, or the stored value is no RSA key.
92
+ */
93
+ export async function sourceKeyOf(secrets, mint) {
94
+ let stored = await secrets.get(SOURCE_KEY);
95
+ if (stored === undefined && mint) {
96
+ const pair = await sourceKeyPair();
97
+ stored = await secrets.ensure(SOURCE_KEY, () => pair.privateKey);
98
+ }
99
+ return stored === undefined ? undefined : sshPublicKey(stored);
100
+ }
101
+ /** Mints the instance token a host's wakeup checks it by, once, when none is stored. */
102
+ export const mintWakeupToken = (secrets) => secrets.ensure(WAKEUP_TOKEN, mintInstanceToken);
103
+ /**
104
+ * The declared repository a host that leaves out its `source` runs from: of the types its
105
+ * `source` reference names, the one `origin` is, else the one the host's own data file declares.
106
+ */
107
+ export function sourceRepository(host, type, resources, origin) {
108
+ const named = [type.fields.references?.[type.host.inputs.source]?.type ?? []].flat();
109
+ const repositories = resources.filter((o) => named.includes(`${o.vendor}.${o.type}`));
110
+ const at = githubRepository(origin);
111
+ const beside = repositories.filter((o) => o.file === host.file);
112
+ return (repositories.find((o) => at !== undefined && o.identity === at) ?? (beside.length === 1 ? beside[0] : undefined));
113
+ }
114
+ /** The declared wakeup that names `host` (its `inputs.host` refers to it), if any. */
115
+ export function wokenBy(host, resources, types) {
116
+ return resources.find((o) => {
117
+ const wakeup = types.get(`${o.vendor}.${o.type}`)?.wakeup;
118
+ return wakeup !== undefined && o.refs[wakeup.inputs.host] === host.id;
119
+ });
120
+ }
121
+ /** Sets `value` at the dotted `path` of `into`, making the objects on the way. */
122
+ function setAt(into, path, value) {
123
+ const keys = path.split(".");
124
+ let at = into;
125
+ for (const key of keys.slice(0, -1))
126
+ at = (at[key] ??= {});
127
+ at[keys.at(-1)] = value;
128
+ }
129
+ /** A declaration's export: `host` of `resources/sanoma/infra.ts#host`. */
130
+ const exportOf = (id) => id.slice(id.indexOf("#") + 1);
131
+ /** A repository's name as a host's (`HostSpec.inputs.name`): lower-case letters, digits and dashes. */
132
+ const nameOf = (repository) => repository
133
+ .toLowerCase()
134
+ .replaceAll(/[^a-z0-9-]+/g, "-")
135
+ .replaceAll(/^-+|-+$/g, "");
136
+ /**
137
+ * Fills, in place, each declared host that derives (`HostSpec.derives`) with what it is not told,
138
+ * and puts the deploy key it clones with before it in `resources`:
139
+ *
140
+ * - `name`, left out (when its type takes `inputs.name`): the name it is bound by, else its
141
+ * repository's (`nameOf`), unless another host of its type has that name;
142
+ * - `image`, left out: the runtime image of the installed `sanoma` (`runtimeImage`);
143
+ * - `source`, left out: the one repository its own data file declares (a reference to it, of a
144
+ * type its `source` names), else the repository the app runs from (`origin`);
145
+ * - `domains`: each public site's domain (`siteDomains`);
146
+ * - `secrets` (unless it binds its own bootstrap), markers only: the declared `database`'s URL as
147
+ * `SANOMA_DATABASE_URL`, the runtime's keys (the signing key the config binds, else the host's
148
+ * own, generated at its create; the encryption key with the built-in store), the source key for
149
+ * a GitHub repository it can declare a deploy key on, the wakeup's token (and URL, for
150
+ * `sanoma.wakeup`) when a wakeup names it, the credentials of its own vendor, its repository's
151
+ * and the login sender's, and the company's GitHub App's keys that are stored;
152
+ * - for a GitHub repository, a `github.repository_deploy_key` (`sourceKeyId`), read-only, whose
153
+ * key is the stored source key's public half, when one is and the config has the type; found by
154
+ * its type's identity (the repository and the key), so a key GitHub holds already is bound, not
155
+ * made again.
156
+ *
157
+ * `ref` is resolved by apply (and drift) with the revision: the repository's default branch.
158
+ * `out.keep` takes the deploy-key ids not declared this read (no source key stored yet, or its
159
+ * host left out), which the caller must not orphan; `out.leftOut` the hosts with a problem, which
160
+ * the caller leaves out, with what names them, so apply changes nothing it cannot give all it needs.
161
+ * Problems: a host with no `database`, a name (none to tell, or another host's), an image or a
162
+ * source that cannot be told, a credential its vendor's driver reads from this machine's
163
+ * environment that nothing binds, a private repository it has no deploy key for, two hosts that
164
+ * would attach the sites' domains.
165
+ */
166
+ export function deriveHosts(resources, types, bindings, facts, out = {}) {
167
+ const problems = [];
168
+ // One host attaches the sites' domains: two would each try to attach them.
169
+ const attaching = resources.filter((r) => {
170
+ const host = types.get(`${r.vendor}.${r.type}`)?.host;
171
+ return host?.derives && host.inputs.domains !== undefined;
172
+ });
173
+ if (attaching.length > 1 && Object.keys(facts.domains).length) {
174
+ for (const r of attaching) {
175
+ out.leftOut?.add(r.id);
176
+ problems.push({
177
+ ...r.at,
178
+ message: `${r.at.export}: ${attaching.length} hosts would attach the public sites' domains: declare one host that derives, the production one`,
179
+ });
180
+ }
181
+ }
182
+ const deployKey = types.get(DEPLOY_KEY_TYPE);
183
+ // A copy: the deploy keys are put in `resources` as it goes.
184
+ for (const r of resources.slice()) {
185
+ const type = types.get(`${r.vendor}.${r.type}`);
186
+ if (!type?.host?.derives)
187
+ continue;
188
+ const spec = type.host;
189
+ const { inputs } = spec;
190
+ let blocked = out.leftOut?.has(r.id) ?? false;
191
+ const problem = (message) => {
192
+ blocked = true;
193
+ problems.push({ ...r.at, message: `${r.at.export}: ${message}` });
194
+ };
195
+ const { desired, refs } = r;
196
+ if (inputs.image !== undefined && valueAt(desired, inputs.image) == null) {
197
+ if (facts.version)
198
+ setAt(desired, inputs.image, runtimeImage(facts.version));
199
+ else
200
+ problem("its runtime image is the installed sanoma's, and sanoma is not installed: install it, or declare `image`");
201
+ }
202
+ if (valueAt(desired, inputs.source) == null) {
203
+ const declared = sourceRepository(r, type, resources, facts.origin);
204
+ if (declared) {
205
+ setAt(desired, inputs.source, declared.name);
206
+ refs[inputs.source] = declared.id;
207
+ }
208
+ else if (facts.origin)
209
+ setAt(desired, inputs.source, facts.origin);
210
+ else
211
+ problem("its source is the repository the app runs from, and the repository has no origin: push it, or declare `source`");
212
+ }
213
+ // The GitHub repository it clones, `owner/name`: a declared one's identity, else its URL's.
214
+ const repositoryId = refs[inputs.source];
215
+ const repository = repositoryId
216
+ ? resources.find((o) => o.id === repositoryId && o.vendor === "github")?.identity
217
+ : githubRepository(valueAt(desired, inputs.source));
218
+ if (inputs.name !== undefined && valueAt(desired, inputs.name) == null) {
219
+ const source = valueAt(desired, inputs.source);
220
+ const named = repository ?? (typeof source === "string" ? cloneUrl(source) : undefined);
221
+ // A bound host keeps the name it is bound by: a renamed repository would otherwise replace it.
222
+ const bound = type.identity === inputs.name ? bindings[r.id]?.id : undefined;
223
+ const name = bound ??
224
+ (named &&
225
+ nameOf(named
226
+ .replace(/\.git$/, "")
227
+ .split("/")
228
+ .at(-1)));
229
+ if (name) {
230
+ setAt(desired, inputs.name, name);
231
+ const identity = type.find?.(desired);
232
+ const twin = identity &&
233
+ resources.find((o) => o !== r && o.vendor === r.vendor && o.type === r.type && o.identity === identity);
234
+ if (twin)
235
+ problem(`its name, its repository's, is ${twin.at.export}'s too, ${name}: declare \`name\``);
236
+ else if (identity)
237
+ Object.assign(r, { identity, name: identity });
238
+ }
239
+ else
240
+ problem("its name is its repository's, and it has none to name it by: declare `name`");
241
+ }
242
+ if (inputs.domains !== undefined && Object.keys(facts.domains).length) {
243
+ setAt(desired, inputs.domains, { ...facts.domains });
244
+ }
245
+ if (spec.bindsBootstrap || inputs.secrets === undefined) {
246
+ if (blocked)
247
+ out.leftOut?.add(r.id);
248
+ continue;
249
+ }
250
+ // A deploy key is declared for a GitHub repository only where the config has the type.
251
+ const keyed = repository !== undefined && deployKey !== undefined;
252
+ if (repository && !keyed && facts.privateRepositories?.includes(repository)) {
253
+ problem(`${repository} is private, and the config has no ${DEPLOY_KEY_TYPE} to give the host a key to clone it with: add the GitHub connector (@sanoma-ai/connector-github) to sanoma.config.ts`);
254
+ }
255
+ const secrets = {};
256
+ const database = inputs.database === undefined ? undefined : refs[inputs.database];
257
+ if (database)
258
+ secrets.SANOMA_DATABASE_URL = secret(databaseUrlSecret(exportOf(database)));
259
+ else if (inputs.database !== undefined)
260
+ problem("declare the database it runs on, `database: store`: the host is given its URL");
261
+ secrets.SANOMA_SIGNING_KEY = facts.signingKeyBound
262
+ ? secret("SANOMA_SIGNING_KEY")
263
+ : secret(HOST_SIGNING_KEY, { generate: true });
264
+ if (facts.builtInStore)
265
+ secrets.SANOMA_SECRETS_KEY = secret("SANOMA_SECRETS_KEY");
266
+ if (keyed)
267
+ secrets[SOURCE_KEY] = secret(SOURCE_KEY);
268
+ const wakeup = wokenBy(r, resources, types);
269
+ if (wakeup) {
270
+ secrets[WAKEUP_TOKEN] = secret(WAKEUP_TOKEN);
271
+ if (wakeup.vendor === "sanoma")
272
+ secrets[WAKEUP_URL] = secret(WAKEUP_URL);
273
+ }
274
+ // What it deploys itself with after a push: the vendors of its data file, its repository's, the sender's.
275
+ const vendors = new Set([
276
+ ...resources.filter((o) => o.file === r.file).map((o) => o.vendor),
277
+ ...(repository ? ["github"] : []),
278
+ ...(facts.sender ? [facts.sender] : []),
279
+ ]);
280
+ const carried = [...vendors].flatMap((vendor) => facts.carried.get(vendor) ?? []);
281
+ for (const name of [...carried, ...facts.githubApp])
282
+ secrets[name] = secret(name);
283
+ // One the driver reads here from the environment alone would be left off the host, and a
284
+ // Worker updated without it would lose it.
285
+ for (const vendor of vendors) {
286
+ for (const name of facts.unbound?.get(vendor) ?? []) {
287
+ problem(`${name} is read from the environment here, and has no binding, so the host would not be given it: bind it in sanoma.config.ts, \`secrets: { bindings: { ${name}: { env: "${name}" } } }\`, or store it on the ${vendor} connector page`);
288
+ }
289
+ }
290
+ setAt(desired, inputs.secrets, secrets);
291
+ const id = sourceKeyId(r.id);
292
+ // Kept from orphaning while it cannot be declared: no source key stored yet, or the host left out.
293
+ if (keyed && (!facts.sourceKey || blocked))
294
+ out.keep?.add(id);
295
+ if (blocked)
296
+ out.leftOut?.add(r.id);
297
+ // Not twice: a data file of before may declare the same key itself (GitHub takes a key once).
298
+ const declaredKey = resources.some((o) => `${o.vendor}.${o.type}` === DEPLOY_KEY_TYPE && o.desired.key === facts.sourceKey);
299
+ if (keyed && facts.sourceKey && !blocked && !declaredKey) {
300
+ const binding = bindings[id]?.id;
301
+ const key = { repository, title: DEPLOY_KEY_TITLE, key: facts.sourceKey, read_only: true };
302
+ const identity = deployKey.find?.(key);
303
+ resources.splice(resources.indexOf(r), 0, {
304
+ id,
305
+ file: r.file,
306
+ vendor: "github",
307
+ type: "repository_deploy_key",
308
+ name: exportOf(id),
309
+ ...(identity && { identity }),
310
+ ...(binding !== undefined && { binding }),
311
+ at: { file: r.at.file, export: exportOf(id) },
312
+ desired: key,
313
+ refs: repositoryId ? { repository: repositoryId } : {},
314
+ });
315
+ }
316
+ }
317
+ return problems;
318
+ }
319
+ /**
320
+ * Whether GitHub shows `repository` (`owner/name`) to anyone, asked with no token: false for one
321
+ * that is private (or not there), undefined when GitHub does not say.
322
+ */
323
+ async function publicOnGithub(repository) {
324
+ try {
325
+ const res = await fetch(`https://api.github.com/repos/${repository}`, {
326
+ method: "HEAD",
327
+ headers: { accept: "application/vnd.github+json" },
328
+ signal: AbortSignal.timeout(10_000),
329
+ });
330
+ return res.status === 200 ? true : res.status === 404 ? false : undefined;
331
+ }
332
+ catch {
333
+ return undefined;
334
+ }
335
+ }
336
+ /**
337
+ * What the declared hosts that derive (`deriveHosts`) are filled from, read now: the installed
338
+ * `sanoma`, `origin`, the public sites' domains, and of `secrets` (none outside a run) which
339
+ * optional credentials, App keys and source key are stored, and which credentials the drivers
340
+ * read from this machine's environment alone (`unbound`). With `mint` (apply, never drift or a
341
+ * sandbox run), the source key (where the config has the deploy-key type) and, when a wakeup is
342
+ * declared, the wakeup's token are minted first when none is stored, so the host is given them
343
+ * from its first apply. With no deploy-key type, GitHub is asked which repositories a host may clone are private.
344
+ * Undefined when no declared host derives.
345
+ */
346
+ export async function hostFacts(options) {
347
+ const { root, resources, types, drivers, secrets, mint } = options;
348
+ const typeOf = (r) => types.get(`${r.vendor}.${r.type}`);
349
+ const deriving = resources.filter((r) => typeOf(r)?.host?.derives);
350
+ if (!deriving.length)
351
+ return undefined;
352
+ const hosts = deriving.filter((r) => !typeOf(r)?.host?.bindsBootstrap);
353
+ // Which optional credentials and App keys are stored: read at once, and only for a host given secrets.
354
+ const stored = async (names) => secrets && hosts.length
355
+ ? new Set((await Promise.all(names.map(async (n) => ((await secrets.get(n)) === undefined ? [] : [n])))).flat())
356
+ : new Set();
357
+ const statuses = [...drivers].flatMap(([vendor, { env }]) => env ? statusesOf(env, () => undefined).map((c) => ({ vendor, ...c })) : []);
358
+ // Only what the store holds: a marker for a name it does not would fail the host's deploy.
359
+ const held = await stored(statuses.map((c) => c.name));
360
+ const carried = new Map();
361
+ const unbound = new Map();
362
+ for (const c of statuses) {
363
+ // The driver reads the environment first: set there alone, it runs here and the host is not given it.
364
+ const into = held.has(c.name) ? carried : secrets && hosts.length && process.env[c.name] ? unbound : undefined;
365
+ into?.set(c.vendor, [...(into.get(c.vendor) ?? []), c.name]);
366
+ }
367
+ const githubApp = [...(await stored(Object.values(GITHUB_APP)))];
368
+ if (mint && secrets && hosts.some((h) => wokenBy(h, resources, types)))
369
+ await mintWakeupToken(secrets);
370
+ const keyed = types.has(DEPLOY_KEY_TYPE);
371
+ const { siteDomains } = await import("./sites.js");
372
+ const [version, origin, sourceKey] = await Promise.all([
373
+ installedSanoma(root),
374
+ originUrl(root),
375
+ secrets && hosts.length && keyed ? sourceKeyOf(secrets, mint) : undefined,
376
+ ]);
377
+ // What a host may clone: `origin`, else the `source` it declares (with no deploy-key type, no
378
+ // GitHub repository is declared to be one).
379
+ const repositories = keyed
380
+ ? []
381
+ : [
382
+ ...new Set([
383
+ githubRepository(origin),
384
+ ...hosts.map((h) => githubRepository(valueAt(h.desired, typeOf(h).host.inputs.source))),
385
+ ].filter((r) => r !== undefined)),
386
+ ];
387
+ const open = await Promise.all(repositories.map(publicOnGithub));
388
+ const privateRepositories = repositories.filter((_, i) => open[i] === false);
389
+ return {
390
+ ...(version !== undefined && { version }),
391
+ ...(origin !== undefined && { origin }),
392
+ domains: siteDomains(root),
393
+ builtInStore: options.builtInStore,
394
+ carried,
395
+ ...(unbound.size > 0 && { unbound }),
396
+ ...(options.signingKeyBound && { signingKeyBound: true }),
397
+ ...(options.sender !== undefined && { sender: options.sender }),
398
+ githubApp,
399
+ ...(sourceKey !== undefined && { sourceKey }),
400
+ ...(privateRepositories.length > 0 && { privateRepositories }),
401
+ };
402
+ }
403
+ //# sourceMappingURL=host-derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-derive.js","sourceRoot":"","sources":["../src/host-derive.ts"],"names":[],"mappings":"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"}