@treeseed/sdk 0.6.39 → 0.6.40
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/capacity.d.ts +53 -0
- package/dist/capacity.js +100 -0
- package/dist/control-plane-client.d.ts +41 -1
- package/dist/control-plane-client.js +154 -0
- package/dist/control-plane.d.ts +6 -1
- package/dist/control-plane.js +39 -2
- package/dist/d1-store.d.ts +63 -1
- package/dist/d1-store.js +190 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -0
- package/dist/operations/services/config-runtime.js +2 -2
- package/dist/operations/services/deploy.js +3 -2
- package/dist/operations/services/knowledge-coop-launch.js +5 -28
- package/dist/operations/services/package-reference-policy.d.ts +68 -0
- package/dist/operations/services/package-reference-policy.js +135 -0
- package/dist/operations/services/project-platform.d.ts +14 -0
- package/dist/operations/services/project-platform.js +3 -2
- package/dist/operations/services/railway-api.d.ts +33 -0
- package/dist/operations/services/railway-api.js +273 -0
- package/dist/operations/services/railway-deploy.d.ts +22 -0
- package/dist/operations/services/railway-deploy.js +81 -19
- package/dist/operations/services/release-candidate.d.ts +2 -0
- package/dist/operations/services/release-candidate.js +28 -0
- package/dist/operations/services/runtime-tools.js +1 -1
- package/dist/operations-registry.js +1 -0
- package/dist/reconcile/bootstrap-systems.js +1 -1
- package/dist/reconcile/builtin-adapters.js +5 -9
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.js +9 -17
- package/dist/reconcile/state.js +4 -4
- package/dist/reconcile/units.js +4 -8
- package/dist/sdk-types.d.ts +566 -3
- package/dist/sdk.d.ts +12 -1
- package/dist/sdk.js +44 -0
- package/dist/stores/operational-store.d.ts +12 -1
- package/dist/stores/operational-store.js +283 -5
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +5 -24
- package/dist/types/agents.d.ts +27 -0
- package/dist/workflow/operations.d.ts +94 -2
- package/dist/workflow/operations.js +90 -32
- package/dist/workflow-state.js +3 -5
- package/dist/workflow.d.ts +8 -1
- package/dist/workflow.js +6 -0
- package/package.json +5 -1
|
@@ -244,6 +244,13 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
|
|
|
244
244
|
healthcheckPath: string | null;
|
|
245
245
|
healthcheckTimeoutSeconds: number | null;
|
|
246
246
|
runtimeMode: string | null;
|
|
247
|
+
volume: {
|
|
248
|
+
id: string;
|
|
249
|
+
name: string;
|
|
250
|
+
mountPath: any;
|
|
251
|
+
created: boolean;
|
|
252
|
+
updated: boolean;
|
|
253
|
+
} | null;
|
|
247
254
|
} | null;
|
|
248
255
|
} | undefined)[];
|
|
249
256
|
}>;
|
|
@@ -737,6 +744,13 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
|
|
|
737
744
|
healthcheckPath: string | null;
|
|
738
745
|
healthcheckTimeoutSeconds: number | null;
|
|
739
746
|
runtimeMode: string | null;
|
|
747
|
+
volume: {
|
|
748
|
+
id: string;
|
|
749
|
+
name: string;
|
|
750
|
+
mountPath: any;
|
|
751
|
+
created: boolean;
|
|
752
|
+
updated: boolean;
|
|
753
|
+
} | null;
|
|
740
754
|
} | null;
|
|
741
755
|
} | undefined)[];
|
|
742
756
|
}>;
|
|
@@ -631,9 +631,10 @@ function probeR2(tenantRoot, siteConfig, state, target) {
|
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
633
|
function probeScaleConfiguration(siteConfig, state) {
|
|
634
|
-
const worker = state.services?.worker ?? {};
|
|
634
|
+
const worker = state.services?.workerRunner ?? state.services?.worker ?? {};
|
|
635
|
+
const workerConfig = siteConfig.services?.workerRunner ?? siteConfig.services?.worker ?? {};
|
|
635
636
|
const scalerKind = String(process.env.TREESEED_WORKER_POOL_SCALER ?? "").trim();
|
|
636
|
-
if (scalerKind !== "railway" &&
|
|
637
|
+
if (scalerKind !== "railway" && workerConfig.provider !== "railway") {
|
|
637
638
|
return {
|
|
638
639
|
ok: true,
|
|
639
640
|
skipped: true,
|
|
@@ -53,6 +53,20 @@ export type RailwayCustomDomainSummary = {
|
|
|
53
53
|
verificationToken: string | null;
|
|
54
54
|
dnsRecords: RailwayCustomDomainDnsRecord[];
|
|
55
55
|
};
|
|
56
|
+
export type RailwayVolumeInstanceSummary = {
|
|
57
|
+
id: string;
|
|
58
|
+
serviceId: string | null;
|
|
59
|
+
environmentId: string | null;
|
|
60
|
+
mountPath: string | null;
|
|
61
|
+
sizeGb: number | null;
|
|
62
|
+
usedGb: number | null;
|
|
63
|
+
};
|
|
64
|
+
export type RailwayVolumeSummary = {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
projectId: string | null;
|
|
68
|
+
instances: RailwayVolumeInstanceSummary[];
|
|
69
|
+
};
|
|
56
70
|
export declare function isUsableRailwayToken(value: string | undefined | null): boolean;
|
|
57
71
|
export declare function resolveRailwayApiToken(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): string;
|
|
58
72
|
export declare function resolveRailwayApiUrl(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): string;
|
|
@@ -235,6 +249,25 @@ export declare function upsertRailwayVariables({ projectId, environmentId, servi
|
|
|
235
249
|
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
236
250
|
fetchImpl?: typeof fetch;
|
|
237
251
|
}): Promise<void>;
|
|
252
|
+
export declare function listRailwayVolumes({ projectId, env, fetchImpl, }: {
|
|
253
|
+
projectId: string;
|
|
254
|
+
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
255
|
+
fetchImpl?: typeof fetch;
|
|
256
|
+
}): Promise<RailwayVolumeSummary[]>;
|
|
257
|
+
export declare function ensureRailwayServiceVolume({ projectId, environmentId, serviceId, name, mountPath, env, fetchImpl, }: {
|
|
258
|
+
projectId: string;
|
|
259
|
+
environmentId: string;
|
|
260
|
+
serviceId: string;
|
|
261
|
+
name: string;
|
|
262
|
+
mountPath: string;
|
|
263
|
+
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
264
|
+
fetchImpl?: typeof fetch;
|
|
265
|
+
}): Promise<{
|
|
266
|
+
volume: RailwayVolumeSummary;
|
|
267
|
+
instance: RailwayVolumeInstanceSummary | null;
|
|
268
|
+
created: boolean;
|
|
269
|
+
updated: boolean;
|
|
270
|
+
}>;
|
|
238
271
|
export declare function listRailwayCustomDomains({ projectId, environmentId, serviceId, env, fetchImpl, }: {
|
|
239
272
|
projectId: string;
|
|
240
273
|
environmentId: string;
|
|
@@ -191,6 +191,83 @@ function normalizeRailwayCustomDomain(node) {
|
|
|
191
191
|
dnsRecords
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
|
+
function normalizeRailwayVolumeInstance(node) {
|
|
195
|
+
const id = railwayConnectionLabel(node.id);
|
|
196
|
+
if (!id) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const sizeGb = normalizeRailwayNumber(node.sizeGb ?? node.sizeGB ?? node.size_gb ?? node.capacityGb ?? node.capacityGB);
|
|
200
|
+
const usedGb = normalizeRailwayNumber(node.usedGb ?? node.usedGB ?? node.used_gb ?? node.currentUsageGb ?? node.currentUsageGB);
|
|
201
|
+
return {
|
|
202
|
+
id,
|
|
203
|
+
serviceId: railwayConnectionLabel(node.serviceId) || railwayConnectionLabel(node.service?.id) || null,
|
|
204
|
+
environmentId: railwayConnectionLabel(node.environmentId) || railwayConnectionLabel(node.environment?.id) || null,
|
|
205
|
+
mountPath: railwayConnectionLabel(node.mountPath) || railwayConnectionLabel(node.mount_path) || null,
|
|
206
|
+
sizeGb,
|
|
207
|
+
usedGb
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function normalizeVolumeInstances(value) {
|
|
211
|
+
const direct = Array.isArray(value) ? value : null;
|
|
212
|
+
if (direct) {
|
|
213
|
+
return direct.map((entry) => entry && typeof entry === "object" ? normalizeRailwayVolumeInstance(entry) : null).filter(Boolean);
|
|
214
|
+
}
|
|
215
|
+
return normalizeConnectionNodes(value, normalizeRailwayVolumeInstance);
|
|
216
|
+
}
|
|
217
|
+
function normalizeRailwayVolume(node) {
|
|
218
|
+
const id = railwayConnectionLabel(node.id);
|
|
219
|
+
if (!id) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
id,
|
|
224
|
+
name: railwayConnectionLabel(node.name),
|
|
225
|
+
projectId: railwayConnectionLabel(node.projectId) || null,
|
|
226
|
+
instances: [
|
|
227
|
+
...normalizeVolumeInstances(node.instances),
|
|
228
|
+
...normalizeVolumeInstances(node.volumeInstances),
|
|
229
|
+
...normalizeVolumeInstances(node.volume_instances)
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function collectRailwayVolumes(value, seen = /* @__PURE__ */ new Set()) {
|
|
234
|
+
const volumes = [];
|
|
235
|
+
const visit = (entry) => {
|
|
236
|
+
if (!entry || typeof entry !== "object") {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (seen.has(entry)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
seen.add(entry);
|
|
243
|
+
if (Array.isArray(entry)) {
|
|
244
|
+
for (const item of entry) {
|
|
245
|
+
visit(item);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const record = entry;
|
|
250
|
+
const volume = normalizeRailwayVolume(record);
|
|
251
|
+
if (volume && (record.volumeInstances !== void 0 || record.instances !== void 0 || record.projectId !== void 0 || record.name !== void 0)) {
|
|
252
|
+
volumes.push(volume);
|
|
253
|
+
}
|
|
254
|
+
for (const child of Object.values(record)) {
|
|
255
|
+
visit(child);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
visit(value);
|
|
259
|
+
const byId = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const volume of volumes) {
|
|
261
|
+
const existing = byId.get(volume.id);
|
|
262
|
+
byId.set(volume.id, existing ? {
|
|
263
|
+
...existing,
|
|
264
|
+
name: existing.name || volume.name,
|
|
265
|
+
projectId: existing.projectId || volume.projectId,
|
|
266
|
+
instances: [...existing.instances, ...volume.instances]
|
|
267
|
+
} : volume);
|
|
268
|
+
}
|
|
269
|
+
return [...byId.values()];
|
|
270
|
+
}
|
|
194
271
|
async function railwayGraphqlRequest({
|
|
195
272
|
query,
|
|
196
273
|
variables,
|
|
@@ -811,6 +888,200 @@ mutation TreeseedRailwayVariableCollectionUpsert($input: VariableCollectionUpser
|
|
|
811
888
|
fetchImpl
|
|
812
889
|
});
|
|
813
890
|
}
|
|
891
|
+
async function listRailwayVolumes({
|
|
892
|
+
projectId,
|
|
893
|
+
env = process.env,
|
|
894
|
+
fetchImpl = fetch
|
|
895
|
+
}) {
|
|
896
|
+
const query = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_LIST_QUERY") || `
|
|
897
|
+
query TreeseedRailwayVolumeList($projectId: String!) {
|
|
898
|
+
project(id: $projectId) {
|
|
899
|
+
id
|
|
900
|
+
volumes {
|
|
901
|
+
edges {
|
|
902
|
+
node {
|
|
903
|
+
id
|
|
904
|
+
name
|
|
905
|
+
projectId
|
|
906
|
+
volumeInstances {
|
|
907
|
+
edges {
|
|
908
|
+
node {
|
|
909
|
+
id
|
|
910
|
+
serviceId
|
|
911
|
+
environmentId
|
|
912
|
+
mountPath
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
`.trim();
|
|
922
|
+
const payload = await railwayGraphqlRequest({
|
|
923
|
+
query,
|
|
924
|
+
variables: { projectId },
|
|
925
|
+
env,
|
|
926
|
+
fetchImpl
|
|
927
|
+
});
|
|
928
|
+
return collectRailwayVolumes(payload.data);
|
|
929
|
+
}
|
|
930
|
+
async function createRailwayVolume({
|
|
931
|
+
projectId,
|
|
932
|
+
environmentId,
|
|
933
|
+
serviceId,
|
|
934
|
+
name,
|
|
935
|
+
mountPath,
|
|
936
|
+
env = process.env,
|
|
937
|
+
fetchImpl = fetch
|
|
938
|
+
}) {
|
|
939
|
+
const mutation = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_CREATE_MUTATION") || `
|
|
940
|
+
mutation TreeseedRailwayVolumeCreate($input: VolumeCreateInput!) {
|
|
941
|
+
volumeCreate(input: $input) {
|
|
942
|
+
id
|
|
943
|
+
name
|
|
944
|
+
projectId
|
|
945
|
+
volumeInstances {
|
|
946
|
+
edges {
|
|
947
|
+
node {
|
|
948
|
+
id
|
|
949
|
+
serviceId
|
|
950
|
+
environmentId
|
|
951
|
+
mountPath
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
`.trim();
|
|
958
|
+
const payload = await railwayGraphqlRequest({
|
|
959
|
+
query: mutation,
|
|
960
|
+
variables: {
|
|
961
|
+
input: {
|
|
962
|
+
projectId,
|
|
963
|
+
environmentId,
|
|
964
|
+
serviceId,
|
|
965
|
+
name,
|
|
966
|
+
mountPath
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
env,
|
|
970
|
+
fetchImpl
|
|
971
|
+
});
|
|
972
|
+
const volume = collectRailwayVolumes(payload.data)[0] ?? null;
|
|
973
|
+
if (!volume) {
|
|
974
|
+
throw new Error(`Railway volume create did not return a usable volume for ${name}.`);
|
|
975
|
+
}
|
|
976
|
+
return volume;
|
|
977
|
+
}
|
|
978
|
+
async function updateRailwayVolumeName({
|
|
979
|
+
volumeId,
|
|
980
|
+
name,
|
|
981
|
+
env = process.env,
|
|
982
|
+
fetchImpl = fetch
|
|
983
|
+
}) {
|
|
984
|
+
const mutation = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_UPDATE_MUTATION") || `
|
|
985
|
+
mutation TreeseedRailwayVolumeUpdate($id: String!, $input: VolumeUpdateInput!) {
|
|
986
|
+
volumeUpdate(id: $id, input: $input) {
|
|
987
|
+
id
|
|
988
|
+
name
|
|
989
|
+
projectId
|
|
990
|
+
volumeInstances {
|
|
991
|
+
edges {
|
|
992
|
+
node {
|
|
993
|
+
id
|
|
994
|
+
serviceId
|
|
995
|
+
environmentId
|
|
996
|
+
mountPath
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
`.trim();
|
|
1003
|
+
const payload = await railwayGraphqlRequest({
|
|
1004
|
+
query: mutation,
|
|
1005
|
+
variables: {
|
|
1006
|
+
id: volumeId,
|
|
1007
|
+
input: { name }
|
|
1008
|
+
},
|
|
1009
|
+
env,
|
|
1010
|
+
fetchImpl
|
|
1011
|
+
});
|
|
1012
|
+
return collectRailwayVolumes(payload.data)[0] ?? null;
|
|
1013
|
+
}
|
|
1014
|
+
async function updateRailwayVolumeInstanceMountPath({
|
|
1015
|
+
instanceId,
|
|
1016
|
+
mountPath,
|
|
1017
|
+
env = process.env,
|
|
1018
|
+
fetchImpl = fetch
|
|
1019
|
+
}) {
|
|
1020
|
+
const mutation = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_INSTANCE_UPDATE_MUTATION") || `
|
|
1021
|
+
mutation TreeseedRailwayVolumeInstanceUpdate($id: String!, $input: VolumeInstanceUpdateInput!) {
|
|
1022
|
+
volumeInstanceUpdate(id: $id, input: $input) {
|
|
1023
|
+
id
|
|
1024
|
+
serviceId
|
|
1025
|
+
environmentId
|
|
1026
|
+
mountPath
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
`.trim();
|
|
1030
|
+
await railwayGraphqlRequest({
|
|
1031
|
+
query: mutation,
|
|
1032
|
+
variables: {
|
|
1033
|
+
id: instanceId,
|
|
1034
|
+
input: { mountPath }
|
|
1035
|
+
},
|
|
1036
|
+
env,
|
|
1037
|
+
fetchImpl
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
async function ensureRailwayServiceVolume({
|
|
1041
|
+
projectId,
|
|
1042
|
+
environmentId,
|
|
1043
|
+
serviceId,
|
|
1044
|
+
name,
|
|
1045
|
+
mountPath,
|
|
1046
|
+
env = process.env,
|
|
1047
|
+
fetchImpl = fetch
|
|
1048
|
+
}) {
|
|
1049
|
+
if (!mountPath.startsWith("/")) {
|
|
1050
|
+
throw new Error(`Railway volume mount path must be absolute: ${mountPath}`);
|
|
1051
|
+
}
|
|
1052
|
+
const volumes = await listRailwayVolumes({ projectId, env, fetchImpl });
|
|
1053
|
+
let volume = volumes.find(
|
|
1054
|
+
(candidate) => candidate.instances.some((instance2) => instance2.serviceId === serviceId && instance2.environmentId === environmentId)
|
|
1055
|
+
) ?? volumes.find((candidate) => candidate.name === name) ?? null;
|
|
1056
|
+
let created = false;
|
|
1057
|
+
let updated = false;
|
|
1058
|
+
if (!volume) {
|
|
1059
|
+
volume = await createRailwayVolume({
|
|
1060
|
+
projectId,
|
|
1061
|
+
environmentId,
|
|
1062
|
+
serviceId,
|
|
1063
|
+
name,
|
|
1064
|
+
mountPath,
|
|
1065
|
+
env,
|
|
1066
|
+
fetchImpl
|
|
1067
|
+
});
|
|
1068
|
+
created = true;
|
|
1069
|
+
}
|
|
1070
|
+
if (volume.name && volume.name !== name) {
|
|
1071
|
+
volume = await updateRailwayVolumeName({ volumeId: volume.id, name, env, fetchImpl }) ?? { ...volume, name };
|
|
1072
|
+
updated = true;
|
|
1073
|
+
}
|
|
1074
|
+
const instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? null;
|
|
1075
|
+
if (instance && instance.mountPath !== mountPath) {
|
|
1076
|
+
await updateRailwayVolumeInstanceMountPath({ instanceId: instance.id, mountPath, env, fetchImpl });
|
|
1077
|
+
volume = {
|
|
1078
|
+
...volume,
|
|
1079
|
+
instances: volume.instances.map((entry) => entry.id === instance.id ? { ...entry, mountPath } : entry)
|
|
1080
|
+
};
|
|
1081
|
+
updated = true;
|
|
1082
|
+
}
|
|
1083
|
+
return { volume, instance, created, updated };
|
|
1084
|
+
}
|
|
814
1085
|
async function listRailwayCustomDomains({
|
|
815
1086
|
projectId,
|
|
816
1087
|
environmentId,
|
|
@@ -863,6 +1134,7 @@ export {
|
|
|
863
1134
|
ensureRailwayProject,
|
|
864
1135
|
ensureRailwayService,
|
|
865
1136
|
ensureRailwayServiceInstanceConfiguration,
|
|
1137
|
+
ensureRailwayServiceVolume,
|
|
866
1138
|
getRailwayAuthProfile,
|
|
867
1139
|
getRailwayProject,
|
|
868
1140
|
getRailwayServiceInstance,
|
|
@@ -872,6 +1144,7 @@ export {
|
|
|
872
1144
|
listRailwayProjects,
|
|
873
1145
|
listRailwayServices,
|
|
874
1146
|
listRailwayVariables,
|
|
1147
|
+
listRailwayVolumes,
|
|
875
1148
|
normalizeRailwayEnvironmentName,
|
|
876
1149
|
railwayGraphqlRequest,
|
|
877
1150
|
resolveRailwayApiToken,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { type TreeseedBootstrapTaskPrefix, type TreeseedBootstrapWriter } from './bootstrap-runner.ts';
|
|
2
|
+
export declare function deriveRailwayWorkerRunnerServiceName(projectSlug: any, index?: number): string;
|
|
3
|
+
export declare function deriveRailwayWorkerRunnerVolumeName(serviceName: any): string;
|
|
4
|
+
export declare function railwayServiceRuntimeStartCommand(service: any): any;
|
|
2
5
|
export declare function isUsableRailwayToken(value: any): boolean;
|
|
3
6
|
export declare function resolveRailwayAuthToken(env?: NodeJS.ProcessEnv): string;
|
|
4
7
|
export declare function buildRailwayCommandEnv(env?: NodeJS.ProcessEnv): {
|
|
@@ -55,6 +58,10 @@ export declare function configuredRailwayServices(tenantRoot: any, scope: any):
|
|
|
55
58
|
runtimeMode: any;
|
|
56
59
|
schedule: string[];
|
|
57
60
|
hostingKind: string;
|
|
61
|
+
runnerPool: {
|
|
62
|
+
bootstrapIndex: number;
|
|
63
|
+
volumeMountPath: string;
|
|
64
|
+
} | null;
|
|
58
65
|
} | null)[];
|
|
59
66
|
export declare function configuredRailwayScheduledJobs(tenantRoot: any, scope: any, { phase }?: {
|
|
60
67
|
phase?: string | undefined;
|
|
@@ -95,6 +102,10 @@ export declare function validateRailwayServiceConfiguration(tenantRoot: any, sco
|
|
|
95
102
|
runtimeMode: any;
|
|
96
103
|
schedule: string[];
|
|
97
104
|
hostingKind: string;
|
|
105
|
+
runnerPool: {
|
|
106
|
+
bootstrapIndex: number;
|
|
107
|
+
volumeMountPath: string;
|
|
108
|
+
} | null;
|
|
98
109
|
} | null)[];
|
|
99
110
|
schedules: {
|
|
100
111
|
service: string;
|
|
@@ -134,6 +145,10 @@ export declare function validateRailwayDeployPrerequisites(tenantRoot: any, scop
|
|
|
134
145
|
runtimeMode: any;
|
|
135
146
|
schedule: string[];
|
|
136
147
|
hostingKind: string;
|
|
148
|
+
runnerPool: {
|
|
149
|
+
bootstrapIndex: number;
|
|
150
|
+
volumeMountPath: string;
|
|
151
|
+
} | null;
|
|
137
152
|
} | null)[];
|
|
138
153
|
schedules: {
|
|
139
154
|
service: string;
|
|
@@ -296,5 +311,12 @@ export declare function deployRailwayService(tenantRoot: any, service: any, { dr
|
|
|
296
311
|
healthcheckPath: string | null;
|
|
297
312
|
healthcheckTimeoutSeconds: number | null;
|
|
298
313
|
runtimeMode: string | null;
|
|
314
|
+
volume: {
|
|
315
|
+
id: string;
|
|
316
|
+
name: string;
|
|
317
|
+
mountPath: any;
|
|
318
|
+
created: boolean;
|
|
319
|
+
updated: boolean;
|
|
320
|
+
} | null;
|
|
299
321
|
} | null;
|
|
300
322
|
}>;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ensureRailwayProject,
|
|
11
11
|
ensureRailwayService,
|
|
12
12
|
ensureRailwayServiceInstanceConfiguration,
|
|
13
|
+
ensureRailwayServiceVolume,
|
|
13
14
|
listRailwayEnvironments,
|
|
14
15
|
listRailwayProjects,
|
|
15
16
|
listRailwayServices,
|
|
@@ -18,7 +19,8 @@ import {
|
|
|
18
19
|
resolveRailwayApiToken,
|
|
19
20
|
resolveRailwayApiUrl,
|
|
20
21
|
resolveRailwayWorkspace,
|
|
21
|
-
resolveRailwayWorkspaceContext
|
|
22
|
+
resolveRailwayWorkspaceContext,
|
|
23
|
+
upsertRailwayVariables
|
|
22
24
|
} from "./railway-api.js";
|
|
23
25
|
function normalizeScope(scope) {
|
|
24
26
|
return scope === "prod" ? "prod" : scope === "staging" ? "staging" : "local";
|
|
@@ -26,13 +28,29 @@ function normalizeScope(scope) {
|
|
|
26
28
|
function resolveRailwayEnvironmentForScope(scope, configuredEnvironment) {
|
|
27
29
|
return normalizeRailwayEnvironmentName(configuredEnvironment || normalizeScope(scope));
|
|
28
30
|
}
|
|
29
|
-
const RAILWAY_SERVICE_KEYS = ["api", "
|
|
30
|
-
const HOSTED_PROJECT_SERVICE_KEYS = ["api", "
|
|
31
|
+
const RAILWAY_SERVICE_KEYS = ["api", "workdayManager", "workerRunner"];
|
|
32
|
+
const HOSTED_PROJECT_SERVICE_KEYS = ["api", "workdayManager", "workerRunner"];
|
|
33
|
+
const WORKER_RUNNER_BOOTSTRAP_INDEX = 1;
|
|
34
|
+
const WORKER_RUNNER_VOLUME_MOUNT_PATH = "/data";
|
|
31
35
|
function shouldManageRailwaySchedules(scope, phase = "deploy") {
|
|
32
|
-
|
|
36
|
+
const environment = normalizeRailwayEnvironmentName(scope);
|
|
37
|
+
return phase === "deploy" && (environment === "staging" || environment === "production");
|
|
33
38
|
}
|
|
34
39
|
function railwayServiceNameSuffix(serviceKey) {
|
|
35
|
-
return serviceKey === "
|
|
40
|
+
return serviceKey === "workdayManager" ? "workday-manager" : serviceKey === "workerRunner" ? "worker-runner" : serviceKey;
|
|
41
|
+
}
|
|
42
|
+
function deriveRailwayWorkerRunnerServiceName(projectSlug, index = WORKER_RUNNER_BOOTSTRAP_INDEX) {
|
|
43
|
+
const normalizedIndex = Math.max(1, Number.parseInt(String(index), 10) || WORKER_RUNNER_BOOTSTRAP_INDEX);
|
|
44
|
+
return `${projectSlug}-worker-runner-${String(normalizedIndex).padStart(2, "0")}`;
|
|
45
|
+
}
|
|
46
|
+
function deriveRailwayWorkerRunnerVolumeName(serviceName) {
|
|
47
|
+
return `${serviceName}-data`;
|
|
48
|
+
}
|
|
49
|
+
function railwayServiceRuntimeStartCommand(service) {
|
|
50
|
+
if (service.key === "workdayManager" && Array.isArray(service.schedule) && service.schedule.length > 0) {
|
|
51
|
+
return `node -e "console.log('workday-manager scheduled-only service idle')"`;
|
|
52
|
+
}
|
|
53
|
+
return service.startCommand;
|
|
36
54
|
}
|
|
37
55
|
function normalizeScheduleExpressions(value) {
|
|
38
56
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -455,7 +473,7 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
455
473
|
if (!service || service.enabled === false || (service.provider ?? "railway") !== "railway") {
|
|
456
474
|
return null;
|
|
457
475
|
}
|
|
458
|
-
const defaultRootDir = ["api", "
|
|
476
|
+
const defaultRootDir = ["api", "workdayManager", "workerRunner"].includes(serviceKey) ? "." : "packages/core";
|
|
459
477
|
const serviceRoot = resolve(tenantRoot, service.railway?.rootDir ?? service.rootDir ?? defaultRootDir);
|
|
460
478
|
const railwayEnvironment = resolveRailwayEnvironmentForScope(
|
|
461
479
|
normalizedScope,
|
|
@@ -468,7 +486,7 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
468
486
|
projectId: service.railway?.projectId ?? null,
|
|
469
487
|
projectName: service.railway?.projectName ?? identity.deploymentKey,
|
|
470
488
|
serviceId: service.railway?.serviceId ?? null,
|
|
471
|
-
serviceName: service.railway?.serviceName ?? `${identity.deploymentKey}-${railwayServiceNameSuffix(serviceKey)}
|
|
489
|
+
serviceName: service.railway?.serviceName ?? (serviceKey === "workerRunner" ? deriveRailwayWorkerRunnerServiceName(identity.deploymentKey) : `${identity.deploymentKey}-${railwayServiceNameSuffix(serviceKey)}`),
|
|
472
490
|
rootDir: serviceRoot,
|
|
473
491
|
publicBaseUrl,
|
|
474
492
|
railwayEnvironment,
|
|
@@ -480,7 +498,11 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
480
498
|
restartPolicy: service.railway?.restartPolicy ?? null,
|
|
481
499
|
runtimeMode: service.railway?.runtimeMode ?? null,
|
|
482
500
|
schedule: normalizeScheduleExpressions(service.railway?.schedule),
|
|
483
|
-
hostingKind
|
|
501
|
+
hostingKind,
|
|
502
|
+
runnerPool: serviceKey === "workerRunner" ? {
|
|
503
|
+
bootstrapIndex: WORKER_RUNNER_BOOTSTRAP_INDEX,
|
|
504
|
+
volumeMountPath: WORKER_RUNNER_VOLUME_MOUNT_PATH
|
|
505
|
+
} : null
|
|
484
506
|
};
|
|
485
507
|
}).filter(Boolean);
|
|
486
508
|
}
|
|
@@ -879,7 +901,8 @@ async function resolveRailwayDeployProjectContext(service, { env = process.env }
|
|
|
879
901
|
}
|
|
880
902
|
async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, service, { env = process.env } = {}) {
|
|
881
903
|
const wantsInstanceConfig = service.buildCommand || service.startCommand || service.rootDir || service.healthcheckPath || service.healthcheckTimeoutSeconds !== null || service.healthcheckTimeoutSeconds !== void 0 || service.healthcheckIntervalSeconds !== null || service.healthcheckIntervalSeconds !== void 0 || service.restartPolicy || service.runtimeMode;
|
|
882
|
-
|
|
904
|
+
const wantsRunnerVolume = service.key === "workerRunner";
|
|
905
|
+
if (!wantsInstanceConfig && !wantsRunnerVolume) {
|
|
883
906
|
return null;
|
|
884
907
|
}
|
|
885
908
|
const workspace = await resolveRailwayWorkspaceContext({ env });
|
|
@@ -922,11 +945,11 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
922
945
|
env
|
|
923
946
|
}).then((result) => result.service);
|
|
924
947
|
}
|
|
925
|
-
|
|
948
|
+
const runtimeConfiguration = wantsInstanceConfig ? await ensureRailwayServiceInstanceConfiguration({
|
|
926
949
|
serviceId: railwayService.id,
|
|
927
950
|
environmentId: environment.id,
|
|
928
951
|
buildCommand: service.buildCommand,
|
|
929
|
-
startCommand: service
|
|
952
|
+
startCommand: railwayServiceRuntimeStartCommand(service),
|
|
930
953
|
rootDirectory: relativeRailwayRootDir(tenantRoot, service.rootDir),
|
|
931
954
|
healthcheckPath: service.healthcheckPath,
|
|
932
955
|
healthcheckTimeoutSeconds: service.healthcheckTimeoutSeconds,
|
|
@@ -934,7 +957,42 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
934
957
|
restartPolicy: service.restartPolicy,
|
|
935
958
|
runtimeMode: service.runtimeMode,
|
|
936
959
|
env
|
|
937
|
-
});
|
|
960
|
+
}) : null;
|
|
961
|
+
const volumeMountPath = service.runnerPool?.volumeMountPath ?? WORKER_RUNNER_VOLUME_MOUNT_PATH;
|
|
962
|
+
const volumeConfiguration = wantsRunnerVolume ? await ensureRailwayServiceVolume({
|
|
963
|
+
projectId: project.id,
|
|
964
|
+
environmentId: environment.id,
|
|
965
|
+
serviceId: railwayService.id,
|
|
966
|
+
name: deriveRailwayWorkerRunnerVolumeName(railwayService.name),
|
|
967
|
+
mountPath: volumeMountPath,
|
|
968
|
+
env
|
|
969
|
+
}) : null;
|
|
970
|
+
if (wantsRunnerVolume) {
|
|
971
|
+
await upsertRailwayVariables({
|
|
972
|
+
projectId: project.id,
|
|
973
|
+
environmentId: environment.id,
|
|
974
|
+
serviceId: railwayService.id,
|
|
975
|
+
variables: {
|
|
976
|
+
TREESEED_RUNNER_SERVICE_NAME: railwayService.name,
|
|
977
|
+
TREESEED_RUNNER_VOLUME_ROOT: volumeMountPath,
|
|
978
|
+
TREESEED_RUNNER_VOLUME_NAME: volumeConfiguration?.volume.name ?? deriveRailwayWorkerRunnerVolumeName(railwayService.name),
|
|
979
|
+
TREESEED_WORKER_IDLE_EXIT_MS: configuredEnvValue(env, "TREESEED_WORKER_IDLE_EXIT_MS") || "60000",
|
|
980
|
+
...volumeConfiguration?.volume.id ? { TREESEED_RUNNER_VOLUME_ID: volumeConfiguration.volume.id } : {}
|
|
981
|
+
},
|
|
982
|
+
env
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
return {
|
|
986
|
+
instance: runtimeConfiguration?.instance ?? null,
|
|
987
|
+
updated: Boolean(runtimeConfiguration?.updated || volumeConfiguration?.updated || volumeConfiguration?.created),
|
|
988
|
+
volume: volumeConfiguration ? {
|
|
989
|
+
id: volumeConfiguration.volume.id,
|
|
990
|
+
name: volumeConfiguration.volume.name,
|
|
991
|
+
mountPath: volumeConfiguration.instance?.mountPath ?? volumeMountPath,
|
|
992
|
+
created: volumeConfiguration.created,
|
|
993
|
+
updated: volumeConfiguration.updated
|
|
994
|
+
} : null
|
|
995
|
+
};
|
|
938
996
|
}
|
|
939
997
|
async function deployRailwayService(tenantRoot, service, {
|
|
940
998
|
dryRun = false,
|
|
@@ -954,13 +1012,16 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
954
1012
|
}
|
|
955
1013
|
const deployService = await resolveRailwayDeployProjectContext(service, { env });
|
|
956
1014
|
const plan = planRailwayServiceDeploy(deployService, { env });
|
|
1015
|
+
const commandEnv = buildRailwayCommandEnv({ ...process.env, ...env });
|
|
957
1016
|
const taskPrefix = prefix ?? {
|
|
958
1017
|
scope: normalizeScope(deployService.scope ?? deployService.railwayEnvironment ?? "railway"),
|
|
959
1018
|
system: deployService.key === "api" ? "api" : "agents",
|
|
960
1019
|
task: `${deployService.key}-railway-deploy`,
|
|
961
1020
|
stage: "deploy"
|
|
962
1021
|
};
|
|
963
|
-
const
|
|
1022
|
+
const runtimeConfiguration = await syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
1023
|
+
env: commandEnv
|
|
1024
|
+
});
|
|
964
1025
|
if (deployService.buildCommand) {
|
|
965
1026
|
const buildResult = await runPrefixedCommand("bash", ["-lc", deployService.buildCommand], {
|
|
966
1027
|
cwd: deployService.rootDir,
|
|
@@ -996,9 +1057,6 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
996
1057
|
if (lastFailure) {
|
|
997
1058
|
throw new Error(lastFailure.stderr?.trim() || lastFailure.stdout?.trim() || `railway ${plan.args.join(" ")} failed`);
|
|
998
1059
|
}
|
|
999
|
-
const runtimeConfiguration = await syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
1000
|
-
env: commandEnv
|
|
1001
|
-
});
|
|
1002
1060
|
return {
|
|
1003
1061
|
service: deployService.key,
|
|
1004
1062
|
status: "deployed",
|
|
@@ -1007,9 +1065,10 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1007
1065
|
publicBaseUrl: deployService.publicBaseUrl,
|
|
1008
1066
|
runtimeConfiguration: runtimeConfiguration ? {
|
|
1009
1067
|
updated: runtimeConfiguration.updated,
|
|
1010
|
-
healthcheckPath: runtimeConfiguration.instance
|
|
1011
|
-
healthcheckTimeoutSeconds: runtimeConfiguration.instance
|
|
1012
|
-
runtimeMode: runtimeConfiguration.instance
|
|
1068
|
+
healthcheckPath: runtimeConfiguration.instance?.healthcheckPath ?? null,
|
|
1069
|
+
healthcheckTimeoutSeconds: runtimeConfiguration.instance?.healthcheckTimeoutSeconds ?? null,
|
|
1070
|
+
runtimeMode: runtimeConfiguration.instance?.runtimeMode ?? null,
|
|
1071
|
+
volume: runtimeConfiguration.volume ?? null
|
|
1013
1072
|
} : null
|
|
1014
1073
|
};
|
|
1015
1074
|
}
|
|
@@ -1018,6 +1077,8 @@ export {
|
|
|
1018
1077
|
configuredRailwayScheduledJobs,
|
|
1019
1078
|
configuredRailwayServices,
|
|
1020
1079
|
deployRailwayService,
|
|
1080
|
+
deriveRailwayWorkerRunnerServiceName,
|
|
1081
|
+
deriveRailwayWorkerRunnerVolumeName,
|
|
1021
1082
|
ensureRailwayEnvironmentExists,
|
|
1022
1083
|
ensureRailwayProjectContext,
|
|
1023
1084
|
ensureRailwayProjectExists,
|
|
@@ -1026,6 +1087,7 @@ export {
|
|
|
1026
1087
|
isRailwayTransientFailure,
|
|
1027
1088
|
isUsableRailwayToken,
|
|
1028
1089
|
planRailwayServiceDeploy,
|
|
1090
|
+
railwayServiceRuntimeStartCommand,
|
|
1029
1091
|
resolveRailwayAuthToken,
|
|
1030
1092
|
resolveRailwayDeploymentProfile,
|
|
1031
1093
|
runRailway,
|
|
@@ -8,6 +8,7 @@ export type ReleaseCandidateFailure = {
|
|
|
8
8
|
};
|
|
9
9
|
export type ReleaseCandidateFingerprint = {
|
|
10
10
|
key: string;
|
|
11
|
+
policyVersion: string;
|
|
11
12
|
rootSha: string | null;
|
|
12
13
|
packageShas: Record<string, string | null>;
|
|
13
14
|
plannedVersions: Record<string, string>;
|
|
@@ -36,4 +37,5 @@ export type ReleaseCandidateInput = {
|
|
|
36
37
|
export declare function buildReleaseCandidateFingerprint(input: ReleaseCandidateInput): ReleaseCandidateFingerprint;
|
|
37
38
|
export declare function readCachedReleaseCandidateReport(root: string, key: string): ReleaseCandidateReport | null;
|
|
38
39
|
export declare function writeReleaseCandidateReport(root: string, report: ReleaseCandidateReport): ReleaseCandidateReport;
|
|
40
|
+
export declare function collectReleaseCandidateOutputFailures(line: string): string[];
|
|
39
41
|
export declare function runReleaseCandidateGate(input: ReleaseCandidateInput): Promise<ReleaseCandidateReport>;
|