@rizom/ops 0.2.0-alpha.190 → 0.2.0-alpha.192
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/dist/brains-ops.js +56 -56
- package/dist/index.js +36 -36
- package/package.json +1 -1
- package/templates/rover-pilot/.github/workflows/deploy.yml +10 -0
- package/templates/rover-pilot/deploy/scripts/decrypt-user-secrets.ts +16 -2
- package/templates/rover-pilot/deploy/scripts/resolve-user-config.ts +44 -10
package/package.json
CHANGED
|
@@ -211,11 +211,16 @@ jobs:
|
|
|
211
211
|
|
|
212
212
|
- name: Update Cloudflare DNS
|
|
213
213
|
env:
|
|
214
|
+
CF_ZONE_ID: ${{ steps.user_config.outputs.cloudflare_zone_id }}
|
|
214
215
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
216
|
+
WWW_DOMAIN: ${{ steps.user_config.outputs.www_domain }}
|
|
215
217
|
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
216
218
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
217
219
|
run: |
|
|
218
220
|
bun deploy/scripts/update-dns.ts
|
|
221
|
+
if [ -n "$WWW_DOMAIN" ]; then
|
|
222
|
+
BRAIN_DOMAIN="$WWW_DOMAIN" bun deploy/scripts/update-dns.ts
|
|
223
|
+
fi
|
|
219
224
|
BRAIN_DOMAIN="$PREVIEW_DOMAIN" bun deploy/scripts/update-dns.ts
|
|
220
225
|
|
|
221
226
|
- name: Validate SSH key
|
|
@@ -243,6 +248,7 @@ jobs:
|
|
|
243
248
|
IMAGE_REPOSITORY: ${{ steps.user_config.outputs.image_repository }}
|
|
244
249
|
REGISTRY_USERNAME: ${{ steps.user_config.outputs.registry_username }}
|
|
245
250
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
251
|
+
WWW_DOMAIN: ${{ steps.user_config.outputs.www_domain }}
|
|
246
252
|
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
247
253
|
BRAIN_YAML_PATH: ${{ steps.user_config.outputs.brain_yaml_path }}
|
|
248
254
|
run: kamal setup --skip-push -c deploy/kamal/deploy.yml
|
|
@@ -251,9 +257,13 @@ jobs:
|
|
|
251
257
|
env:
|
|
252
258
|
SERVER_IP: ${{ steps.provision.outputs.server_ip }}
|
|
253
259
|
BRAIN_DOMAIN: ${{ steps.user_config.outputs.brain_domain }}
|
|
260
|
+
WWW_DOMAIN: ${{ steps.user_config.outputs.www_domain }}
|
|
254
261
|
PREVIEW_DOMAIN: ${{ steps.user_config.outputs.preview_domain }}
|
|
255
262
|
run: |
|
|
256
263
|
curl -I -k --max-time 20 --resolve "$BRAIN_DOMAIN:443:$SERVER_IP" "https://$BRAIN_DOMAIN/health"
|
|
264
|
+
if [ -n "$WWW_DOMAIN" ]; then
|
|
265
|
+
curl -I -k --max-time 20 --resolve "$WWW_DOMAIN:443:$SERVER_IP" "https://$WWW_DOMAIN/"
|
|
266
|
+
fi
|
|
257
267
|
curl -I -k --max-time 20 --resolve "$PREVIEW_DOMAIN:443:$SERVER_IP" "https://$PREVIEW_DOMAIN/"
|
|
258
268
|
|
|
259
269
|
- name: Upload generated config
|
|
@@ -28,9 +28,19 @@ writeSecretGitHubEnv("DISCORD_BOT_TOKEN", secrets["discordBotToken"]);
|
|
|
28
28
|
// Per-user AT Protocol publishing credential (optional; from the user's
|
|
29
29
|
// encrypted secrets file). The scaffold wires this so a pilot can publish its
|
|
30
30
|
// brain's agent card to its PDS; a deployment that doesn't publish simply
|
|
31
|
-
// leaves it unset.
|
|
32
|
-
// the kamal proxy block via shared env, not here.
|
|
31
|
+
// leaves it unset.
|
|
33
32
|
writeSecretGitHubEnv("ATPROTO_APP_PASSWORD", secrets["atprotoAppPassword"]);
|
|
33
|
+
// Per-user custom-domain TLS overrides the shared fleet certificate when a
|
|
34
|
+
// complete certificate/key pair is present in the encrypted user secrets.
|
|
35
|
+
const certificatePem = decodeEscapedSecret(secrets["certificatePem"]);
|
|
36
|
+
const privateKeyPem = decodeEscapedSecret(secrets["privateKeyPem"]);
|
|
37
|
+
if (Boolean(certificatePem) !== Boolean(privateKeyPem)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
"Custom-domain TLS secrets require both certificatePem and privateKeyPem",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
writeSecretGitHubEnv("CERTIFICATE_PEM", certificatePem);
|
|
43
|
+
writeSecretGitHubEnv("PRIVATE_KEY_PEM", privateKeyPem);
|
|
34
44
|
|
|
35
45
|
writeGitHubOutput(
|
|
36
46
|
"shared_ai_api_key_secret_name",
|
|
@@ -98,6 +108,10 @@ function parseFlatYaml(contents: string): Record<string, string> {
|
|
|
98
108
|
return result;
|
|
99
109
|
}
|
|
100
110
|
|
|
111
|
+
function decodeEscapedSecret(value: string | undefined): string | undefined {
|
|
112
|
+
return value?.replace(/\\n/g, "\n");
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
function requireFlatValue(
|
|
102
116
|
values: Record<string, string>,
|
|
103
117
|
key: string,
|
|
@@ -25,14 +25,23 @@ if (!brainDomain) {
|
|
|
25
25
|
throw new Error(`Missing domain in ${brainYamlPath}`);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!zone) {
|
|
33
|
-
throw new Error(`Could not derive preview domain from ${brainDomain}`);
|
|
28
|
+
const registry = await loadPilotRegistry(process.cwd());
|
|
29
|
+
const user = registry.users.find((entry) => entry.handle === handle);
|
|
30
|
+
if (!user) {
|
|
31
|
+
throw new Error(`Unknown user handle: ${handle}`);
|
|
34
32
|
}
|
|
35
|
-
const previewDomain =
|
|
33
|
+
const previewDomain = resolvePreviewDomain(
|
|
34
|
+
handle,
|
|
35
|
+
brainDomain,
|
|
36
|
+
registry.pilot.domainSuffix,
|
|
37
|
+
);
|
|
38
|
+
const wwwDomain = isFleetDomain(
|
|
39
|
+
handle,
|
|
40
|
+
brainDomain,
|
|
41
|
+
registry.pilot.domainSuffix,
|
|
42
|
+
)
|
|
43
|
+
? ""
|
|
44
|
+
: `www.${brainDomain}`;
|
|
36
45
|
|
|
37
46
|
const brainVersion = envEntries["BRAIN_VERSION"] ?? "";
|
|
38
47
|
|
|
@@ -40,9 +49,7 @@ const brainVersion = envEntries["BRAIN_VERSION"] ?? "";
|
|
|
40
49
|
// `brain-{version}` for a default instance, or its own `brain-{version}-sites-
|
|
41
50
|
// {hash}` when it declares a siteOverride. Resolved through the same helper the
|
|
42
51
|
// build uses so the tag we wait for and run matches exactly what was pushed.
|
|
43
|
-
const
|
|
44
|
-
const user = registry.users.find((entry) => entry.handle === handle);
|
|
45
|
-
const sitePackages = sitePackagesFor(user?.siteOverride);
|
|
52
|
+
const sitePackages = sitePackagesFor(user.siteOverride);
|
|
46
53
|
const imageTag = siteImageTag(brainVersion, sitePackages);
|
|
47
54
|
|
|
48
55
|
const outputs: Record<string, string> = {
|
|
@@ -50,6 +57,8 @@ const outputs: Record<string, string> = {
|
|
|
50
57
|
content_repo: envEntries["CONTENT_REPO"] ?? "",
|
|
51
58
|
brain_domain: brainDomain,
|
|
52
59
|
preview_domain: previewDomain,
|
|
60
|
+
www_domain: wwwDomain,
|
|
61
|
+
cloudflare_zone_id: user.cloudflareZoneId ?? process.env["CF_ZONE_ID"] ?? "",
|
|
53
62
|
brain_yaml_path: brainYamlPath,
|
|
54
63
|
instance_name: `rover-${handle}`,
|
|
55
64
|
image_repository: `ghcr.io/${repository}`,
|
|
@@ -67,3 +76,28 @@ for (const key of required) {
|
|
|
67
76
|
for (const [key, value] of Object.entries(outputs)) {
|
|
68
77
|
writeGitHubOutput(key, value);
|
|
69
78
|
}
|
|
79
|
+
|
|
80
|
+
function resolvePreviewDomain(
|
|
81
|
+
userHandle: string,
|
|
82
|
+
domain: string,
|
|
83
|
+
pilotDomainSuffix: string,
|
|
84
|
+
): string {
|
|
85
|
+
if (!isFleetDomain(userHandle, domain, pilotDomainSuffix)) {
|
|
86
|
+
return `preview.${domain}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fleetZone = pilotDomainSuffix.replace(/^\./, "");
|
|
90
|
+
if (!fleetZone) {
|
|
91
|
+
throw new Error(`Could not derive preview domain from ${domain}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return `${userHandle}-preview.${fleetZone}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isFleetDomain(
|
|
98
|
+
userHandle: string,
|
|
99
|
+
domain: string,
|
|
100
|
+
pilotDomainSuffix: string,
|
|
101
|
+
): boolean {
|
|
102
|
+
return domain === `${userHandle}${pilotDomainSuffix}`;
|
|
103
|
+
}
|