@treeseed/sdk 0.12.54 → 0.12.55
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/operations/services/railway-deploy.d.ts +1 -1
- package/dist/operations/services/railway-deploy.js +19 -8
- package/dist/reconcile/builtin-adapters.js +47 -0
- package/dist/reconcile/providers/railway-iac.d.ts +4 -1
- package/dist/reconcile/providers/railway-iac.js +20 -1
- package/package.json +1 -1
|
@@ -59,7 +59,7 @@ export declare function waitForRailwayManagedDeploymentsSettled(tenantRoot: any,
|
|
|
59
59
|
};
|
|
60
60
|
message: string;
|
|
61
61
|
}>;
|
|
62
|
-
export declare function configuredRailwayServices(tenantRoot: any, scope: any, envOverlay?: {}): unknown[];
|
|
62
|
+
export declare function configuredRailwayServices(tenantRoot: any, scope: any, envOverlay?: {}, options?: {}): unknown[];
|
|
63
63
|
export declare function configuredRailwayScheduledJobs(tenantRoot: any, scope: any, { phase }?: {
|
|
64
64
|
phase?: string | undefined;
|
|
65
65
|
}): any[];
|
|
@@ -641,8 +641,9 @@ query TreeseedRailwayDeploymentStatus($projectId: String!) {
|
|
|
641
641
|
});
|
|
642
642
|
return payload.data?.project ?? null;
|
|
643
643
|
}
|
|
644
|
-
function configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, application = null, machineConfigRoot = tenantRoot, envOverlay = {}) {
|
|
644
|
+
function configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, application = null, machineConfigRoot = tenantRoot, envOverlay = {}, options = {}) {
|
|
645
645
|
const normalizedScope = normalizeScope(scope);
|
|
646
|
+
const identityOnly = options.identityOnly === true;
|
|
646
647
|
const imageRefKeys = [
|
|
647
648
|
"TREESEED_API_IMAGE_REF",
|
|
648
649
|
"TREESEED_OPERATIONS_RUNNER_IMAGE_REF",
|
|
@@ -702,7 +703,13 @@ function configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, app
|
|
|
702
703
|
const configuredImageRefEnv = service.railway?.imageRefEnv ?? railwayImageRefEnvForService(serviceKey);
|
|
703
704
|
const canUseImageRefEnv = normalizedScope === "prod" || serviceKey === "capacityProviderManager" || serviceKey === "capacityProviderRunner";
|
|
704
705
|
const imageRef = service.railway?.imageRef ?? (canUseImageRefEnv && configuredImageRefEnv ? envValue(configuredImageRefEnv, imageRefEnv) || null : null) ?? defaultRailwayImageRef(serviceKey, normalizedScope, imageRefEnv);
|
|
705
|
-
const sourcePolicy =
|
|
706
|
+
const sourcePolicy = identityOnly ? {
|
|
707
|
+
sourceMode: normalizedScope === "prod" ? "image" : "git",
|
|
708
|
+
sourceRepo: null,
|
|
709
|
+
sourceBranch: null,
|
|
710
|
+
sourceCommit: null,
|
|
711
|
+
sourceRootDirectory: null
|
|
712
|
+
} : resolveRailwayServiceSourcePolicy({
|
|
706
713
|
tenantRoot,
|
|
707
714
|
scope: normalizedScope,
|
|
708
715
|
serviceKey,
|
|
@@ -760,11 +767,12 @@ function configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, app
|
|
|
760
767
|
hostingKind,
|
|
761
768
|
application,
|
|
762
769
|
imageRefEnv,
|
|
763
|
-
workspaceRoot: machineConfigRoot
|
|
770
|
+
workspaceRoot: machineConfigRoot,
|
|
771
|
+
identityOnly
|
|
764
772
|
})
|
|
765
773
|
];
|
|
766
774
|
}
|
|
767
|
-
function configuredPublicTreeDxRailwayServices({ tenantRoot, scope, deployConfig, identity, hostingKind, application, imageRefEnv, workspaceRoot }) {
|
|
775
|
+
function configuredPublicTreeDxRailwayServices({ tenantRoot, scope, deployConfig, identity, hostingKind, application, imageRefEnv, workspaceRoot, identityOnly = false }) {
|
|
768
776
|
if (deployConfig.hosting?.kind !== "treeseed_control_plane") {
|
|
769
777
|
return [];
|
|
770
778
|
}
|
|
@@ -803,7 +811,9 @@ function configuredPublicTreeDxRailwayServices({ tenantRoot, scope, deployConfig
|
|
|
803
811
|
buildCommand: sourceMode === "git" ? railway.buildCommand ?? null : null,
|
|
804
812
|
startCommand: sourceMode === "git" ? railway.startCommand ?? null : null
|
|
805
813
|
};
|
|
806
|
-
|
|
814
|
+
if (!identityOnly) {
|
|
815
|
+
assertApiRailwaySourcePolicy(scope, service);
|
|
816
|
+
}
|
|
807
817
|
return {
|
|
808
818
|
key: service.key,
|
|
809
819
|
instanceKey: serviceName,
|
|
@@ -957,9 +967,9 @@ function resolveRailwayCapacityProviderRoot(tenantRoot, service) {
|
|
|
957
967
|
);
|
|
958
968
|
return found ?? resolve(tenantRoot, "packages", "agent");
|
|
959
969
|
}
|
|
960
|
-
function configuredRailwayServices(tenantRoot, scope, envOverlay = {}) {
|
|
970
|
+
function configuredRailwayServices(tenantRoot, scope, envOverlay = {}, options = {}) {
|
|
961
971
|
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
962
|
-
const direct = configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, null, tenantRoot, envOverlay);
|
|
972
|
+
const direct = configuredRailwayServicesForConfig(tenantRoot, scope, deployConfig, null, tenantRoot, envOverlay, options);
|
|
963
973
|
const nested = discoverTreeseedApplications(tenantRoot).filter((application) => application.root !== resolve(tenantRoot)).flatMap((application) => configuredRailwayServicesForConfig(
|
|
964
974
|
application.root,
|
|
965
975
|
scope,
|
|
@@ -970,7 +980,8 @@ function configuredRailwayServices(tenantRoot, scope, envOverlay = {}) {
|
|
|
970
980
|
relativeRoot: application.relativeRoot
|
|
971
981
|
},
|
|
972
982
|
tenantRoot,
|
|
973
|
-
envOverlay
|
|
983
|
+
envOverlay,
|
|
984
|
+
options
|
|
974
985
|
));
|
|
975
986
|
return [...direct, ...nested];
|
|
976
987
|
}
|
|
@@ -3657,6 +3657,24 @@ function configuredRailwayProjectSyncGroups(input, scope, serviceKeys) {
|
|
|
3657
3657
|
}
|
|
3658
3658
|
return [...grouped.values()];
|
|
3659
3659
|
}
|
|
3660
|
+
function configuredRailwaySiblingResourceNames(input, scope, projectName) {
|
|
3661
|
+
const siblingScope = scope === "prod" ? "staging" : scope === "staging" ? "prod" : null;
|
|
3662
|
+
if (!siblingScope) return [];
|
|
3663
|
+
const siblingServices = configuredRailwayServices(
|
|
3664
|
+
input.context.tenantRoot,
|
|
3665
|
+
siblingScope,
|
|
3666
|
+
resolveReconcileEnvironmentValues(input, siblingScope),
|
|
3667
|
+
{ identityOnly: true }
|
|
3668
|
+
).filter((service) => service.enabled !== false).filter((service) => !isRailwayCapacityProviderService(service)).filter((service) => service.projectName === projectName);
|
|
3669
|
+
const database = configuredRailwayIacDatabase(input, siblingServices);
|
|
3670
|
+
return [.../* @__PURE__ */ new Set([
|
|
3671
|
+
...siblingServices.flatMap((service) => [
|
|
3672
|
+
service.serviceName,
|
|
3673
|
+
...service.volumeMountPath ? [`${service.serviceName}-volume`] : []
|
|
3674
|
+
]),
|
|
3675
|
+
...database ? [database.serviceName, `${database.serviceName}-volume`] : []
|
|
3676
|
+
])];
|
|
3677
|
+
}
|
|
3660
3678
|
function railwayIacServiceInput(input, sync, service, scope) {
|
|
3661
3679
|
if (scope === "staging" && service.imageRef) {
|
|
3662
3680
|
throw new Error(`Railway staging service ${service.serviceName} must build from Git source, not Docker image ${service.imageRef}.`);
|
|
@@ -3931,11 +3949,13 @@ async function syncRailwayEnvironmentForScope(input, { planOnly = false, service
|
|
|
3931
3949
|
})),
|
|
3932
3950
|
database: databaseForIac
|
|
3933
3951
|
};
|
|
3952
|
+
const siblingResourceNames = configuredRailwaySiblingResourceNames(input, scope, project.name);
|
|
3934
3953
|
const {
|
|
3935
3954
|
applyRailwayIacProject,
|
|
3936
3955
|
cleanupRailwayIacRender,
|
|
3937
3956
|
planRailwayIacProject,
|
|
3938
3957
|
renderRailwayIacProject,
|
|
3958
|
+
selectRailwayIacRetainedResources,
|
|
3939
3959
|
validateRailwayIacChangeSet
|
|
3940
3960
|
} = await import("./providers/railway-iac.js");
|
|
3941
3961
|
let effectiveIacInput = iacInput;
|
|
@@ -3986,6 +4006,33 @@ async function syncRailwayEnvironmentForScope(input, { planOnly = false, service
|
|
|
3986
4006
|
]))
|
|
3987
4007
|
});
|
|
3988
4008
|
}
|
|
4009
|
+
const retainedResources = selectRailwayIacRetainedResources(plan, siblingResourceNames);
|
|
4010
|
+
if (retainedResources.length > 0) {
|
|
4011
|
+
traceRailwayReconcile(
|
|
4012
|
+
topology.env,
|
|
4013
|
+
"sync:iac-retain-sibling-environment",
|
|
4014
|
+
`${project.name}/${environment.name}:${retainedResources.map((resource) => resource.name).join(",")}`
|
|
4015
|
+
);
|
|
4016
|
+
cleanupRailwayIacRender(rendered);
|
|
4017
|
+
effectiveIacInput = { ...effectiveIacInput, retainedResources };
|
|
4018
|
+
rendered = renderRailwayIacProject(effectiveIacInput);
|
|
4019
|
+
plan = await planRailwayIacProject(effectiveIacInput, rendered);
|
|
4020
|
+
if (!plan.ok) {
|
|
4021
|
+
const diagnostics = plan.diagnostics.map((entry) => entry.message).filter(Boolean).join("; ");
|
|
4022
|
+
throw new Error(`Railway IaC plan failed for ${project.name}/${environment.name}${diagnostics ? `: ${diagnostics}` : "."}`);
|
|
4023
|
+
}
|
|
4024
|
+
validation = validateRailwayIacChangeSet(plan.changeSet, {
|
|
4025
|
+
services: rendered.serviceNames,
|
|
4026
|
+
volumes: rendered.volumeNames,
|
|
4027
|
+
database: rendered.databaseName,
|
|
4028
|
+
scope,
|
|
4029
|
+
serviceSourceModes: Object.fromEntries(effectiveIacInput.services.map((service) => [service.serviceName, service.sourceMode ?? null])),
|
|
4030
|
+
serviceSourceRefs: Object.fromEntries(effectiveIacInput.services.map((service) => [
|
|
4031
|
+
service.serviceName,
|
|
4032
|
+
service.sourceMode === "git" && service.sourceRepo ? `github:${service.sourceRepo}:${service.sourceBranch ?? ""}:${service.sourceRootDirectory ?? ""}:${service.sourceCommit ?? ""}` : service.sourceMode === "image" && service.imageRef ? `image:${service.imageRef}` : null
|
|
4033
|
+
]))
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
3989
4036
|
if (!validation.ok) {
|
|
3990
4037
|
throw new Error(`Railway IaC plan rejected for ${project.name}/${environment.name}: ${validation.blockedReasons.join("; ")}`);
|
|
3991
4038
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RailwayChangeSet, type RailwayIacApplyResponse, type RailwayIacPlanResponse } from 'railway/iac';
|
|
1
|
+
import { type RailwayChangeSet, type RailwayIacApplyResponse, type RailwayIacPlanResponse, type ResourceNode } from 'railway/iac';
|
|
2
2
|
export type TreeseedRailwayIacService = {
|
|
3
3
|
key: string;
|
|
4
4
|
serviceName: string;
|
|
@@ -37,6 +37,7 @@ export type TreeseedRailwayIacProjectInput = {
|
|
|
37
37
|
railwayApiUrl?: string | null;
|
|
38
38
|
services: TreeseedRailwayIacService[];
|
|
39
39
|
database: TreeseedRailwayIacDatabase | null;
|
|
40
|
+
retainedResources?: ResourceNode[];
|
|
40
41
|
region?: string | null;
|
|
41
42
|
};
|
|
42
43
|
export type TreeseedRailwayIacRenderResult = {
|
|
@@ -47,6 +48,7 @@ export type TreeseedRailwayIacRenderResult = {
|
|
|
47
48
|
serviceNames: string[];
|
|
48
49
|
volumeNames: string[];
|
|
49
50
|
databaseName: string | null;
|
|
51
|
+
retainedResourceNames: string[];
|
|
50
52
|
source: string;
|
|
51
53
|
};
|
|
52
54
|
export type RailwayIacValidationResult = {
|
|
@@ -56,6 +58,7 @@ export type RailwayIacValidationResult = {
|
|
|
56
58
|
allowedDrift: string[];
|
|
57
59
|
};
|
|
58
60
|
export declare function renderRailwayIacProject(input: TreeseedRailwayIacProjectInput): TreeseedRailwayIacRenderResult;
|
|
61
|
+
export declare function selectRailwayIacRetainedResources(plan: Pick<RailwayIacPlanResponse, 'changeSet' | 'currentGraph'>, allowedNames: Iterable<string>): ResourceNode[];
|
|
59
62
|
export declare function validateRailwayIacChangeSet(changeSet: RailwayChangeSet | undefined, desiredNames: {
|
|
60
63
|
services: string[];
|
|
61
64
|
volumes: string[];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
runRailwayIac
|
|
5
|
+
} from "railway/iac";
|
|
4
6
|
import { assertApiRailwaySourcePolicy, isApiRailwaySourcePolicyService } from "../../operations/services/railway-source-policy.js";
|
|
5
7
|
function js(value) {
|
|
6
8
|
return JSON.stringify(value);
|
|
@@ -115,6 +117,16 @@ function renderRailwayIacProject(input) {
|
|
|
115
117
|
const volumeNames = [];
|
|
116
118
|
const databaseVariableName = input.database ? "db" : null;
|
|
117
119
|
const databaseEnvName = input.database?.environmentVariable ?? null;
|
|
120
|
+
const desiredResourceNames = /* @__PURE__ */ new Set([
|
|
121
|
+
...input.services.map((service) => service.serviceName),
|
|
122
|
+
...input.services.filter((service) => service.volumeMountPath).map((service) => `${service.serviceName}-volume`),
|
|
123
|
+
...input.database ? [input.database.serviceName, `${input.database.serviceName}-volume`] : []
|
|
124
|
+
]);
|
|
125
|
+
const retainedResources = (input.retainedResources ?? []).filter((resource) => !desiredResourceNames.has(resource.name));
|
|
126
|
+
if (retainedResources.length > 0) {
|
|
127
|
+
declarations.push(` const retainedResources = ${js(retainedResources)};`);
|
|
128
|
+
resources.push("...retainedResources");
|
|
129
|
+
}
|
|
118
130
|
if (input.database) {
|
|
119
131
|
const postgresVolumeName = `${input.database.serviceName}-volume`;
|
|
120
132
|
const postgresMountPath = input.database.mountPath?.trim() || "/var/lib/postgresql/data";
|
|
@@ -205,9 +217,15 @@ ${declarations.join("\n")}
|
|
|
205
217
|
serviceNames: input.services.map((service) => service.serviceName),
|
|
206
218
|
volumeNames,
|
|
207
219
|
databaseName: input.database?.serviceName ?? null,
|
|
220
|
+
retainedResourceNames: retainedResources.map((resource) => resource.name),
|
|
208
221
|
source
|
|
209
222
|
};
|
|
210
223
|
}
|
|
224
|
+
function selectRailwayIacRetainedResources(plan, allowedNames) {
|
|
225
|
+
const allowed = new Set(allowedNames);
|
|
226
|
+
const deletedAllowedNames = new Set((plan.changeSet?.changes ?? []).filter((change) => change.kind === "resource.delete").map((change) => changeName(change)).filter((name) => allowed.has(name)));
|
|
227
|
+
return (plan.currentGraph?.resources ?? []).filter((resource) => deletedAllowedNames.has(resource.name));
|
|
228
|
+
}
|
|
211
229
|
function changeName(change) {
|
|
212
230
|
return String(change?.resource?.name ?? change?.previous?.name ?? change?.address ?? change?.path ?? "");
|
|
213
231
|
}
|
|
@@ -322,5 +340,6 @@ export {
|
|
|
322
340
|
cleanupRailwayIacRender,
|
|
323
341
|
planRailwayIacProject,
|
|
324
342
|
renderRailwayIacProject,
|
|
343
|
+
selectRailwayIacRetainedResources,
|
|
325
344
|
validateRailwayIacChangeSet
|
|
326
345
|
};
|