@treeseed/sdk 0.6.40 → 0.6.42
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/project-platform.d.ts +4 -4
- package/dist/operations/services/project-platform.js +5 -4
- package/dist/operations/services/railway-api.d.ts +8 -1
- package/dist/operations/services/railway-api.js +10 -1
- package/dist/operations/services/railway-deploy.d.ts +10 -10
- package/dist/operations/services/railway-deploy.js +181 -116
- package/package.json +1 -1
|
@@ -245,8 +245,8 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
|
|
|
245
245
|
healthcheckTimeoutSeconds: number | null;
|
|
246
246
|
runtimeMode: string | null;
|
|
247
247
|
volume: {
|
|
248
|
-
id:
|
|
249
|
-
name:
|
|
248
|
+
id: any;
|
|
249
|
+
name: any;
|
|
250
250
|
mountPath: any;
|
|
251
251
|
created: boolean;
|
|
252
252
|
updated: boolean;
|
|
@@ -745,8 +745,8 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
|
|
|
745
745
|
healthcheckTimeoutSeconds: number | null;
|
|
746
746
|
runtimeMode: string | null;
|
|
747
747
|
volume: {
|
|
748
|
-
id:
|
|
749
|
-
name:
|
|
748
|
+
id: any;
|
|
749
|
+
name: any;
|
|
750
750
|
mountPath: any;
|
|
751
751
|
created: boolean;
|
|
752
752
|
updated: boolean;
|
|
@@ -1120,9 +1120,10 @@ async function deployProjectPlatform(options) {
|
|
|
1120
1120
|
previousRailwayDeployNodeId = nodeId;
|
|
1121
1121
|
}
|
|
1122
1122
|
}
|
|
1123
|
+
const managesRailwaySchedules = options.scope === "staging" || options.scope === "prod";
|
|
1123
1124
|
let railwaySchedules = [];
|
|
1124
|
-
let railwayScheduleVerification = { ok: true, checks: [], skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" :
|
|
1125
|
-
if (
|
|
1125
|
+
let railwayScheduleVerification = { ok: true, checks: [], skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "dry_run" };
|
|
1126
|
+
if (managesRailwaySchedules && selectedSystems.has("agents")) {
|
|
1126
1127
|
const agentDeployNodeIds = nodes.filter((node) => node.id.startsWith("agents:") && node.id.endsWith("-railway-deploy")).map((node) => node.id);
|
|
1127
1128
|
nodes.push({
|
|
1128
1129
|
id: "agents:schedules",
|
|
@@ -1156,9 +1157,9 @@ async function deployProjectPlatform(options) {
|
|
|
1156
1157
|
serviceResults
|
|
1157
1158
|
});
|
|
1158
1159
|
}
|
|
1159
|
-
if (
|
|
1160
|
+
if (!managesRailwaySchedules || !selectedSystems.has("agents")) {
|
|
1160
1161
|
railwaySchedules = [];
|
|
1161
|
-
railwayScheduleVerification = { ok: true, checks: railwaySchedules, skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" :
|
|
1162
|
+
railwayScheduleVerification = { ok: true, checks: railwaySchedules, skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "dry_run" };
|
|
1162
1163
|
}
|
|
1163
1164
|
if (selectedSystems.has("agents")) {
|
|
1164
1165
|
serviceResults.push({
|
|
@@ -22,6 +22,7 @@ export type RailwayServiceInstanceSummary = {
|
|
|
22
22
|
id: string | null;
|
|
23
23
|
buildCommand: string | null;
|
|
24
24
|
startCommand: string | null;
|
|
25
|
+
cronSchedule: string | null;
|
|
25
26
|
rootDirectory: string | null;
|
|
26
27
|
healthcheckPath: string | null;
|
|
27
28
|
healthcheckTimeoutSeconds: number | null;
|
|
@@ -157,6 +158,7 @@ export declare function getRailwayServiceInstance({ serviceId, environmentId, en
|
|
|
157
158
|
id: string | null;
|
|
158
159
|
buildCommand: string | null;
|
|
159
160
|
startCommand: string | null;
|
|
161
|
+
cronSchedule: string | null;
|
|
160
162
|
rootDirectory: string | null;
|
|
161
163
|
healthcheckPath: string | null;
|
|
162
164
|
healthcheckTimeoutSeconds: number | null;
|
|
@@ -169,6 +171,7 @@ export declare function getRailwayServiceInstance({ serviceId, environmentId, en
|
|
|
169
171
|
id: string | null;
|
|
170
172
|
buildCommand: string | null;
|
|
171
173
|
startCommand: string | null;
|
|
174
|
+
cronSchedule: string | null;
|
|
172
175
|
rootDirectory: string | null;
|
|
173
176
|
healthcheckPath: null;
|
|
174
177
|
healthcheckTimeoutSeconds: null;
|
|
@@ -178,11 +181,12 @@ export declare function getRailwayServiceInstance({ serviceId, environmentId, en
|
|
|
178
181
|
sleepApplication: null;
|
|
179
182
|
runtimeConfigSupported: false;
|
|
180
183
|
}>;
|
|
181
|
-
export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, environmentId, buildCommand, startCommand, rootDirectory, healthcheckPath, healthcheckTimeoutSeconds, healthcheckIntervalSeconds, restartPolicy, runtimeMode, env, fetchImpl, }: {
|
|
184
|
+
export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, environmentId, buildCommand, startCommand, cronSchedule, rootDirectory, healthcheckPath, healthcheckTimeoutSeconds, healthcheckIntervalSeconds, restartPolicy, runtimeMode, env, fetchImpl, }: {
|
|
182
185
|
serviceId: string;
|
|
183
186
|
environmentId: string;
|
|
184
187
|
buildCommand?: string | null;
|
|
185
188
|
startCommand?: string | null;
|
|
189
|
+
cronSchedule?: string | null;
|
|
186
190
|
rootDirectory?: string | null;
|
|
187
191
|
healthcheckPath?: string | null;
|
|
188
192
|
healthcheckTimeoutSeconds?: number | null;
|
|
@@ -196,6 +200,7 @@ export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, e
|
|
|
196
200
|
id: string | null;
|
|
197
201
|
buildCommand: string | null;
|
|
198
202
|
startCommand: string | null;
|
|
203
|
+
cronSchedule: string | null;
|
|
199
204
|
rootDirectory: string | null;
|
|
200
205
|
healthcheckPath: string | null;
|
|
201
206
|
healthcheckTimeoutSeconds: number | null;
|
|
@@ -208,6 +213,7 @@ export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, e
|
|
|
208
213
|
id: string | null;
|
|
209
214
|
buildCommand: string | null;
|
|
210
215
|
startCommand: string | null;
|
|
216
|
+
cronSchedule: string | null;
|
|
211
217
|
rootDirectory: string | null;
|
|
212
218
|
healthcheckPath: null;
|
|
213
219
|
healthcheckTimeoutSeconds: null;
|
|
@@ -223,6 +229,7 @@ export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, e
|
|
|
223
229
|
id: string;
|
|
224
230
|
buildCommand: string | null;
|
|
225
231
|
startCommand: string | null;
|
|
232
|
+
cronSchedule: string | null;
|
|
226
233
|
rootDirectory: string | null;
|
|
227
234
|
healthcheckPath: string | null;
|
|
228
235
|
healthcheckTimeoutSeconds: number | null;
|
|
@@ -659,6 +659,7 @@ async function getRailwayServiceInstance({
|
|
|
659
659
|
id: null,
|
|
660
660
|
buildCommand: null,
|
|
661
661
|
startCommand: null,
|
|
662
|
+
cronSchedule: null,
|
|
662
663
|
rootDirectory: null,
|
|
663
664
|
healthcheckPath: null,
|
|
664
665
|
healthcheckTimeoutSeconds: null,
|
|
@@ -676,6 +677,7 @@ query TreeseedRailwayServiceInstance($serviceId: String!, $environmentId: String
|
|
|
676
677
|
id
|
|
677
678
|
buildCommand
|
|
678
679
|
startCommand
|
|
680
|
+
cronSchedule
|
|
679
681
|
rootDirectory
|
|
680
682
|
healthcheckPath
|
|
681
683
|
healthcheckTimeout
|
|
@@ -692,6 +694,7 @@ query TreeseedRailwayServiceInstance($serviceId: String!, $environmentId: String
|
|
|
692
694
|
id: railwayConnectionLabel(instance?.id) || null,
|
|
693
695
|
buildCommand: railwayConnectionLabel(instance?.buildCommand) || null,
|
|
694
696
|
startCommand: railwayConnectionLabel(instance?.startCommand) || null,
|
|
697
|
+
cronSchedule: railwayConnectionLabel(instance?.cronSchedule) || null,
|
|
695
698
|
rootDirectory: railwayConnectionLabel(instance?.rootDirectory) || null,
|
|
696
699
|
healthcheckPath: railwayConnectionLabel(instance?.healthcheckPath) || null,
|
|
697
700
|
healthcheckTimeoutSeconds: normalizeRailwayNumber(instance?.healthcheckTimeout),
|
|
@@ -711,6 +714,7 @@ query TreeseedRailwayServiceInstanceLegacy($serviceId: String!, $environmentId:
|
|
|
711
714
|
id
|
|
712
715
|
buildCommand
|
|
713
716
|
startCommand
|
|
717
|
+
cronSchedule
|
|
714
718
|
rootDirectory
|
|
715
719
|
}
|
|
716
720
|
}
|
|
@@ -725,6 +729,7 @@ query TreeseedRailwayServiceInstanceLegacy($serviceId: String!, $environmentId:
|
|
|
725
729
|
id: railwayConnectionLabel(instance?.id) || null,
|
|
726
730
|
buildCommand: railwayConnectionLabel(instance?.buildCommand) || null,
|
|
727
731
|
startCommand: railwayConnectionLabel(instance?.startCommand) || null,
|
|
732
|
+
cronSchedule: railwayConnectionLabel(instance?.cronSchedule) || null,
|
|
728
733
|
rootDirectory: railwayConnectionLabel(instance?.rootDirectory) || null
|
|
729
734
|
};
|
|
730
735
|
}
|
|
@@ -739,6 +744,7 @@ async function ensureRailwayServiceInstanceConfiguration({
|
|
|
739
744
|
environmentId,
|
|
740
745
|
buildCommand,
|
|
741
746
|
startCommand,
|
|
747
|
+
cronSchedule,
|
|
742
748
|
rootDirectory,
|
|
743
749
|
healthcheckPath,
|
|
744
750
|
healthcheckTimeoutSeconds,
|
|
@@ -755,6 +761,7 @@ async function ensureRailwayServiceInstanceConfiguration({
|
|
|
755
761
|
const desired = {
|
|
756
762
|
buildCommand: railwayConnectionLabel(buildCommand) || null,
|
|
757
763
|
startCommand: railwayConnectionLabel(startCommand) || null,
|
|
764
|
+
cronSchedule: railwayConnectionLabel(cronSchedule) || null,
|
|
758
765
|
rootDirectory: railwayConnectionLabel(rootDirectory) || null,
|
|
759
766
|
healthcheckPath: railwayConnectionLabel(healthcheckPath) || null,
|
|
760
767
|
healthcheckTimeoutSeconds: normalizeRailwayNumber(healthcheckTimeoutSeconds),
|
|
@@ -773,7 +780,7 @@ async function ensureRailwayServiceInstanceConfiguration({
|
|
|
773
780
|
if (desired.restartPolicy !== null) {
|
|
774
781
|
throw new Error("Railway service instance restart policies are unsupported by the current Railway API schema.");
|
|
775
782
|
}
|
|
776
|
-
const drifted = desired.buildCommand !== null && desired.buildCommand !== current.buildCommand || desired.startCommand !== null && desired.startCommand !== current.startCommand || desired.rootDirectory !== null && desired.rootDirectory !== current.rootDirectory || desired.healthcheckPath !== null && desired.healthcheckPath !== current.healthcheckPath || desired.healthcheckTimeoutSeconds !== null && desired.healthcheckTimeoutSeconds !== current.healthcheckTimeoutSeconds || desired.runtimeMode !== null && desired.runtimeMode !== current.runtimeMode;
|
|
783
|
+
const drifted = desired.buildCommand !== null && desired.buildCommand !== current.buildCommand || desired.startCommand !== null && desired.startCommand !== current.startCommand || desired.cronSchedule !== null && desired.cronSchedule !== current.cronSchedule || desired.rootDirectory !== null && desired.rootDirectory !== current.rootDirectory || desired.healthcheckPath !== null && desired.healthcheckPath !== current.healthcheckPath || desired.healthcheckTimeoutSeconds !== null && desired.healthcheckTimeoutSeconds !== current.healthcheckTimeoutSeconds || desired.runtimeMode !== null && desired.runtimeMode !== current.runtimeMode;
|
|
777
784
|
if (!drifted) {
|
|
778
785
|
return { instance: current, updated: false };
|
|
779
786
|
}
|
|
@@ -795,6 +802,7 @@ mutation TreeseedRailwayServiceInstanceUpdateLegacy($serviceId: String!, $enviro
|
|
|
795
802
|
input: {
|
|
796
803
|
...desired.buildCommand !== null ? { buildCommand: desired.buildCommand } : {},
|
|
797
804
|
...desired.startCommand !== null ? { startCommand: desired.startCommand } : {},
|
|
805
|
+
...desired.cronSchedule !== null ? { cronSchedule: desired.cronSchedule } : {},
|
|
798
806
|
...desired.rootDirectory !== null ? { rootDirectory: desired.rootDirectory } : {},
|
|
799
807
|
...desired.healthcheckPath !== null ? { healthcheckPath: desired.healthcheckPath } : {},
|
|
800
808
|
...desired.healthcheckTimeoutSeconds !== null ? { healthcheckTimeout: desired.healthcheckTimeoutSeconds } : {},
|
|
@@ -822,6 +830,7 @@ mutation TreeseedRailwayServiceInstanceUpdateLegacy($serviceId: String!, $enviro
|
|
|
822
830
|
id: instance.id || current.id,
|
|
823
831
|
buildCommand: instance.buildCommand ?? desired.buildCommand,
|
|
824
832
|
startCommand: instance.startCommand ?? desired.startCommand,
|
|
833
|
+
cronSchedule: instance.cronSchedule ?? desired.cronSchedule,
|
|
825
834
|
rootDirectory: instance.rootDirectory ?? desired.rootDirectory,
|
|
826
835
|
healthcheckPath: instance.healthcheckPath ?? desired.healthcheckPath,
|
|
827
836
|
healthcheckTimeoutSeconds: instance.healthcheckTimeoutSeconds ?? desired.healthcheckTimeoutSeconds,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type TreeseedBootstrapTaskPrefix, type TreeseedBootstrapWriter } from './bootstrap-runner.ts';
|
|
2
2
|
export declare function deriveRailwayWorkerRunnerServiceName(projectSlug: any, index?: number): string;
|
|
3
|
-
export declare function deriveRailwayWorkerRunnerVolumeName(serviceName: any): string;
|
|
3
|
+
export declare function deriveRailwayWorkerRunnerVolumeName(serviceName: any, environmentName?: string): string;
|
|
4
4
|
export declare function railwayServiceRuntimeStartCommand(service: any): any;
|
|
5
5
|
export declare function isUsableRailwayToken(value: any): boolean;
|
|
6
6
|
export declare function resolveRailwayAuthToken(env?: NodeJS.ProcessEnv): string;
|
|
@@ -201,10 +201,10 @@ export declare function ensureRailwayScheduledJobs(tenantRoot: any, scope: any,
|
|
|
201
201
|
logicalName: string;
|
|
202
202
|
} | {
|
|
203
203
|
projectId: string;
|
|
204
|
-
id:
|
|
204
|
+
id: string | null;
|
|
205
205
|
status: string;
|
|
206
|
-
enabled:
|
|
207
|
-
command:
|
|
206
|
+
enabled: boolean;
|
|
207
|
+
command: string | null;
|
|
208
208
|
serviceId: string;
|
|
209
209
|
environmentId: string;
|
|
210
210
|
service: string;
|
|
@@ -257,16 +257,16 @@ export declare function verifyRailwayScheduledJobs(tenantRoot: any, scope: any,
|
|
|
257
257
|
enabled: boolean;
|
|
258
258
|
logicalName: string;
|
|
259
259
|
} | {
|
|
260
|
-
id:
|
|
260
|
+
id: string | null;
|
|
261
261
|
projectId: string;
|
|
262
262
|
serviceId: string;
|
|
263
263
|
environmentId: string;
|
|
264
264
|
ok: boolean;
|
|
265
265
|
status: string;
|
|
266
266
|
observed: {
|
|
267
|
-
expression:
|
|
268
|
-
command:
|
|
269
|
-
enabled:
|
|
267
|
+
expression: string | null;
|
|
268
|
+
command: string | null;
|
|
269
|
+
enabled: boolean;
|
|
270
270
|
} | null;
|
|
271
271
|
message: string | undefined;
|
|
272
272
|
service: string;
|
|
@@ -312,8 +312,8 @@ export declare function deployRailwayService(tenantRoot: any, service: any, { dr
|
|
|
312
312
|
healthcheckTimeoutSeconds: number | null;
|
|
313
313
|
runtimeMode: string | null;
|
|
314
314
|
volume: {
|
|
315
|
-
id:
|
|
316
|
-
name:
|
|
315
|
+
id: any;
|
|
316
|
+
name: any;
|
|
317
317
|
mountPath: any;
|
|
318
318
|
created: boolean;
|
|
319
319
|
updated: boolean;
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
ensureRailwayService,
|
|
12
12
|
ensureRailwayServiceInstanceConfiguration,
|
|
13
13
|
ensureRailwayServiceVolume,
|
|
14
|
+
getRailwayServiceInstance,
|
|
14
15
|
listRailwayEnvironments,
|
|
15
16
|
listRailwayProjects,
|
|
16
17
|
listRailwayServices,
|
|
17
18
|
normalizeRailwayEnvironmentName,
|
|
18
|
-
railwayGraphqlRequest,
|
|
19
19
|
resolveRailwayApiToken,
|
|
20
20
|
resolveRailwayApiUrl,
|
|
21
21
|
resolveRailwayWorkspace,
|
|
@@ -43,13 +43,12 @@ function deriveRailwayWorkerRunnerServiceName(projectSlug, index = WORKER_RUNNER
|
|
|
43
43
|
const normalizedIndex = Math.max(1, Number.parseInt(String(index), 10) || WORKER_RUNNER_BOOTSTRAP_INDEX);
|
|
44
44
|
return `${projectSlug}-worker-runner-${String(normalizedIndex).padStart(2, "0")}`;
|
|
45
45
|
}
|
|
46
|
-
function deriveRailwayWorkerRunnerVolumeName(serviceName) {
|
|
47
|
-
|
|
46
|
+
function deriveRailwayWorkerRunnerVolumeName(serviceName, environmentName = "") {
|
|
47
|
+
const environment = normalizeRailwayEnvironmentName(environmentName);
|
|
48
|
+
const environmentSuffix = environment && environment !== "production" ? `-${environment}` : "";
|
|
49
|
+
return `${serviceName}${environmentSuffix}-data`;
|
|
48
50
|
}
|
|
49
51
|
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
52
|
return service.startCommand;
|
|
54
53
|
}
|
|
55
54
|
function normalizeScheduleExpressions(value) {
|
|
@@ -73,6 +72,61 @@ function configuredEnvValue(env, name) {
|
|
|
73
72
|
const value = env?.[name];
|
|
74
73
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
75
74
|
}
|
|
75
|
+
function parseRailwayJsonOutput(output) {
|
|
76
|
+
const trimmed = typeof output === "string" ? output.trim() : "";
|
|
77
|
+
if (!trimmed) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(trimmed);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
const lines = trimmed.split(/\r?\n/u);
|
|
85
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
86
|
+
const candidate = lines.slice(index).join("\n").trim();
|
|
87
|
+
if (!candidate.startsWith("{") && !candidate.startsWith("[")) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(candidate);
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function normalizeRailwayCliVolume(value, { serviceId, environmentId, fallbackName, fallbackMountPath }) {
|
|
98
|
+
if (!value || typeof value !== "object") {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const record = value;
|
|
102
|
+
const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : "";
|
|
103
|
+
if (!id) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : fallbackName;
|
|
107
|
+
const mountPath = typeof record.mountPath === "string" && record.mountPath.trim() ? record.mountPath.trim() : fallbackMountPath;
|
|
108
|
+
const sizeMb = typeof record.sizeMB === "number" ? record.sizeMB : null;
|
|
109
|
+
const currentSizeMb = typeof record.currentSizeMB === "number" ? record.currentSizeMB : null;
|
|
110
|
+
return {
|
|
111
|
+
id,
|
|
112
|
+
name,
|
|
113
|
+
projectId: null,
|
|
114
|
+
instances: [{
|
|
115
|
+
id,
|
|
116
|
+
serviceId,
|
|
117
|
+
environmentId,
|
|
118
|
+
mountPath,
|
|
119
|
+
sizeGb: sizeMb === null ? null : sizeMb / 1e3,
|
|
120
|
+
usedGb: currentSizeMb === null ? null : currentSizeMb / 1e3
|
|
121
|
+
}]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function normalizeRailwayCliVolumeList(value, options) {
|
|
125
|
+
if (!value || typeof value !== "object" || !Array.isArray(value.volumes)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return value.volumes.map((entry) => normalizeRailwayCliVolume(entry, options)).filter(Boolean);
|
|
129
|
+
}
|
|
76
130
|
function isUsableRailwayToken(value) {
|
|
77
131
|
return typeof value === "string" && value.trim().length >= 8;
|
|
78
132
|
}
|
|
@@ -636,7 +690,6 @@ async function ensureRailwayScheduledJobs(tenantRoot, scope, { dryRun = false, f
|
|
|
636
690
|
if (typeof effectiveApiToken !== "string" || effectiveApiToken.trim().length === 0) {
|
|
637
691
|
throw new Error("Configure RAILWAY_API_TOKEN before deploying Railway-managed services.");
|
|
638
692
|
}
|
|
639
|
-
const queries = defaultRailwayScheduleQueries();
|
|
640
693
|
const results = [];
|
|
641
694
|
try {
|
|
642
695
|
for (const schedule of schedules) {
|
|
@@ -658,21 +711,12 @@ async function ensureRailwayScheduledJobs(tenantRoot, scope, { dryRun = false, f
|
|
|
658
711
|
});
|
|
659
712
|
continue;
|
|
660
713
|
}
|
|
661
|
-
const
|
|
662
|
-
projectId: target.project.id,
|
|
714
|
+
const current = await getRailwayServiceInstance({
|
|
663
715
|
serviceId: target.service.id,
|
|
664
|
-
environmentId: target.environment.id
|
|
665
|
-
|
|
666
|
-
const listed = await railwayGraphqlRequest({
|
|
667
|
-
query: queries.listQuery,
|
|
668
|
-
variables,
|
|
669
|
-
apiToken: effectiveApiToken,
|
|
670
|
-
apiUrl: effectiveApiUrl,
|
|
716
|
+
environmentId: target.environment.id,
|
|
717
|
+
env: { ...env, RAILWAY_API_TOKEN: effectiveApiToken, TREESEED_RAILWAY_API_URL: effectiveApiUrl },
|
|
671
718
|
fetchImpl
|
|
672
719
|
});
|
|
673
|
-
const existing = collectRailwaySchedules(listed?.data).find(
|
|
674
|
-
(entry) => entry.id && entry.id === schedule.id || entry.name && entry.name === schedule.logicalName || entry.expression === schedule.expression && entry.serviceId === schedule.serviceId && (!schedule.environmentId || entry.environmentId === schedule.environmentId)
|
|
675
|
-
);
|
|
676
720
|
const desired = {
|
|
677
721
|
name: schedule.logicalName,
|
|
678
722
|
schedule: schedule.expression,
|
|
@@ -680,90 +724,38 @@ async function ensureRailwayScheduledJobs(tenantRoot, scope, { dryRun = false, f
|
|
|
680
724
|
enabled: schedule.enabled !== false
|
|
681
725
|
};
|
|
682
726
|
const drifted = Boolean(
|
|
683
|
-
|
|
727
|
+
current.id && (current.cronSchedule !== desired.schedule || (current.startCommand ?? null) !== (desired.command ?? null))
|
|
684
728
|
);
|
|
685
729
|
if (dryRun) {
|
|
686
730
|
results.push({
|
|
687
731
|
...schedule,
|
|
688
|
-
projectId:
|
|
689
|
-
id:
|
|
690
|
-
status:
|
|
732
|
+
projectId: target.project.id,
|
|
733
|
+
id: current.id,
|
|
734
|
+
status: current.id ? drifted ? "planned_update" : "planned_noop" : "planned_create",
|
|
691
735
|
enabled: desired.enabled,
|
|
692
736
|
command: desired.command,
|
|
693
|
-
serviceId:
|
|
694
|
-
environmentId:
|
|
695
|
-
});
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
if (!existing) {
|
|
699
|
-
const created = await railwayGraphqlRequest({
|
|
700
|
-
query: queries.createMutation,
|
|
701
|
-
variables: {
|
|
702
|
-
...variables,
|
|
703
|
-
name: desired.name,
|
|
704
|
-
schedule: desired.schedule,
|
|
705
|
-
command: desired.command,
|
|
706
|
-
enabled: desired.enabled
|
|
707
|
-
},
|
|
708
|
-
apiToken: effectiveApiToken,
|
|
709
|
-
apiUrl: effectiveApiUrl,
|
|
710
|
-
fetchImpl
|
|
711
|
-
});
|
|
712
|
-
const createdSchedule = collectRailwaySchedules(created?.data)[0];
|
|
713
|
-
if (!createdSchedule?.id) {
|
|
714
|
-
throw new Error(`Railway schedule create did not return an id for ${schedule.logicalName}.`);
|
|
715
|
-
}
|
|
716
|
-
results.push({
|
|
717
|
-
...schedule,
|
|
718
|
-
projectId: variables.projectId,
|
|
719
|
-
id: createdSchedule.id,
|
|
720
|
-
status: "created",
|
|
721
|
-
enabled: createdSchedule.enabled,
|
|
722
|
-
command: createdSchedule.command ?? desired.command,
|
|
723
|
-
serviceId: variables.serviceId,
|
|
724
|
-
environmentId: variables.environmentId
|
|
725
|
-
});
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
if (drifted) {
|
|
729
|
-
const updated = await railwayGraphqlRequest({
|
|
730
|
-
query: queries.updateMutation,
|
|
731
|
-
variables: {
|
|
732
|
-
id: existing.id,
|
|
733
|
-
name: desired.name,
|
|
734
|
-
schedule: desired.schedule,
|
|
735
|
-
command: desired.command,
|
|
736
|
-
enabled: desired.enabled
|
|
737
|
-
},
|
|
738
|
-
apiToken: effectiveApiToken,
|
|
739
|
-
apiUrl: effectiveApiUrl,
|
|
740
|
-
fetchImpl
|
|
741
|
-
});
|
|
742
|
-
const updatedSchedule = collectRailwaySchedules(updated?.data)[0];
|
|
743
|
-
if (!updatedSchedule?.id) {
|
|
744
|
-
throw new Error(`Railway schedule update did not return an id for ${schedule.logicalName}.`);
|
|
745
|
-
}
|
|
746
|
-
results.push({
|
|
747
|
-
...schedule,
|
|
748
|
-
projectId: variables.projectId,
|
|
749
|
-
id: updatedSchedule.id,
|
|
750
|
-
status: "updated",
|
|
751
|
-
enabled: updatedSchedule.enabled,
|
|
752
|
-
command: updatedSchedule.command ?? desired.command,
|
|
753
|
-
serviceId: variables.serviceId,
|
|
754
|
-
environmentId: variables.environmentId
|
|
737
|
+
serviceId: target.service.id,
|
|
738
|
+
environmentId: target.environment.id
|
|
755
739
|
});
|
|
756
740
|
continue;
|
|
757
741
|
}
|
|
742
|
+
const updated = await ensureRailwayServiceInstanceConfiguration({
|
|
743
|
+
serviceId: target.service.id,
|
|
744
|
+
environmentId: target.environment.id,
|
|
745
|
+
startCommand: desired.command,
|
|
746
|
+
cronSchedule: desired.schedule,
|
|
747
|
+
env: { ...env, RAILWAY_API_TOKEN: effectiveApiToken, TREESEED_RAILWAY_API_URL: effectiveApiUrl },
|
|
748
|
+
fetchImpl
|
|
749
|
+
});
|
|
758
750
|
results.push({
|
|
759
751
|
...schedule,
|
|
760
|
-
projectId:
|
|
761
|
-
id:
|
|
762
|
-
status: "noop",
|
|
763
|
-
enabled:
|
|
764
|
-
command:
|
|
765
|
-
serviceId:
|
|
766
|
-
environmentId:
|
|
752
|
+
projectId: target.project.id,
|
|
753
|
+
id: updated.instance.id,
|
|
754
|
+
status: updated.updated ? "updated" : "noop",
|
|
755
|
+
enabled: desired.enabled,
|
|
756
|
+
command: desired.command,
|
|
757
|
+
serviceId: target.service.id,
|
|
758
|
+
environmentId: target.environment.id
|
|
767
759
|
});
|
|
768
760
|
}
|
|
769
761
|
} catch (error) {
|
|
@@ -785,7 +777,6 @@ async function verifyRailwayScheduledJobs(tenantRoot, scope, { fetchImpl = fetch
|
|
|
785
777
|
const effectiveApiToken = apiToken || env?.RAILWAY_API_TOKEN;
|
|
786
778
|
const effectiveApiUrl = apiUrl || resolveRailwayApiUrl(env);
|
|
787
779
|
const configured = configuredRailwayScheduledJobs(tenantRoot, scope);
|
|
788
|
-
const queries = defaultRailwayScheduleQueries();
|
|
789
780
|
const checks = [];
|
|
790
781
|
try {
|
|
791
782
|
for (const schedule of configured) {
|
|
@@ -804,36 +795,28 @@ async function verifyRailwayScheduledJobs(tenantRoot, scope, { fetchImpl = fetch
|
|
|
804
795
|
});
|
|
805
796
|
continue;
|
|
806
797
|
}
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
serviceId: target.service.id,
|
|
812
|
-
environmentId: target.environment.id
|
|
813
|
-
},
|
|
814
|
-
apiToken: effectiveApiToken,
|
|
815
|
-
apiUrl: effectiveApiUrl,
|
|
798
|
+
const existing = await getRailwayServiceInstance({
|
|
799
|
+
serviceId: target.service.id,
|
|
800
|
+
environmentId: target.environment.id,
|
|
801
|
+
env: { ...env, RAILWAY_API_TOKEN: effectiveApiToken, TREESEED_RAILWAY_API_URL: effectiveApiUrl },
|
|
816
802
|
fetchImpl
|
|
817
803
|
});
|
|
818
|
-
const existing = collectRailwaySchedules(listed?.data).find(
|
|
819
|
-
(entry) => entry.name && entry.name === schedule.logicalName || entry.expression === schedule.expression && entry.serviceId === schedule.serviceId && (!schedule.environmentId || entry.environmentId === schedule.environmentId)
|
|
820
|
-
);
|
|
821
804
|
checks.push({
|
|
822
805
|
...schedule,
|
|
823
|
-
id: existing
|
|
806
|
+
id: existing.id,
|
|
824
807
|
projectId: target.project.id,
|
|
825
808
|
serviceId: target.service.id,
|
|
826
809
|
environmentId: target.environment.id,
|
|
827
810
|
ok: Boolean(
|
|
828
|
-
existing && existing.
|
|
811
|
+
existing.id && existing.cronSchedule === schedule.expression && (existing.startCommand ?? null) === (schedule.command ?? null)
|
|
829
812
|
),
|
|
830
|
-
status: existing ? "checked" : "missing",
|
|
831
|
-
observed: existing ? {
|
|
832
|
-
expression: existing.
|
|
833
|
-
command: existing.
|
|
834
|
-
enabled:
|
|
813
|
+
status: existing.id ? "checked" : "missing",
|
|
814
|
+
observed: existing.id ? {
|
|
815
|
+
expression: existing.cronSchedule,
|
|
816
|
+
command: existing.startCommand ?? null,
|
|
817
|
+
enabled: true
|
|
835
818
|
} : null,
|
|
836
|
-
message: existing ? void 0 : `Railway schedule ${schedule.logicalName} is missing for ${target.service.name} in ${target.environment.name}.`
|
|
819
|
+
message: existing.id ? void 0 : `Railway schedule ${schedule.logicalName} is missing for ${target.service.name} in ${target.environment.name}.`
|
|
837
820
|
});
|
|
838
821
|
}
|
|
839
822
|
} catch (error) {
|
|
@@ -950,6 +933,7 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
950
933
|
environmentId: environment.id,
|
|
951
934
|
buildCommand: service.buildCommand,
|
|
952
935
|
startCommand: railwayServiceRuntimeStartCommand(service),
|
|
936
|
+
cronSchedule: service.schedule?.[0] ?? null,
|
|
953
937
|
rootDirectory: relativeRailwayRootDir(tenantRoot, service.rootDir),
|
|
954
938
|
healthcheckPath: service.healthcheckPath,
|
|
955
939
|
healthcheckTimeoutSeconds: service.healthcheckTimeoutSeconds,
|
|
@@ -959,11 +943,14 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
959
943
|
env
|
|
960
944
|
}) : null;
|
|
961
945
|
const volumeMountPath = service.runnerPool?.volumeMountPath ?? WORKER_RUNNER_VOLUME_MOUNT_PATH;
|
|
962
|
-
const volumeConfiguration = wantsRunnerVolume ? await
|
|
946
|
+
const volumeConfiguration = wantsRunnerVolume ? await ensureRailwayServiceVolumeWithCliFallback({
|
|
947
|
+
tenantRoot,
|
|
963
948
|
projectId: project.id,
|
|
964
949
|
environmentId: environment.id,
|
|
950
|
+
environmentName: environment.name,
|
|
965
951
|
serviceId: railwayService.id,
|
|
966
|
-
|
|
952
|
+
serviceName: railwayService.name,
|
|
953
|
+
name: deriveRailwayWorkerRunnerVolumeName(railwayService.name, environment.name),
|
|
967
954
|
mountPath: volumeMountPath,
|
|
968
955
|
env
|
|
969
956
|
}) : null;
|
|
@@ -975,7 +962,7 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
975
962
|
variables: {
|
|
976
963
|
TREESEED_RUNNER_SERVICE_NAME: railwayService.name,
|
|
977
964
|
TREESEED_RUNNER_VOLUME_ROOT: volumeMountPath,
|
|
978
|
-
TREESEED_RUNNER_VOLUME_NAME: volumeConfiguration?.volume.name ?? deriveRailwayWorkerRunnerVolumeName(railwayService.name),
|
|
965
|
+
TREESEED_RUNNER_VOLUME_NAME: volumeConfiguration?.volume.name ?? deriveRailwayWorkerRunnerVolumeName(railwayService.name, environment.name),
|
|
979
966
|
TREESEED_WORKER_IDLE_EXIT_MS: configuredEnvValue(env, "TREESEED_WORKER_IDLE_EXIT_MS") || "60000",
|
|
980
967
|
...volumeConfiguration?.volume.id ? { TREESEED_RUNNER_VOLUME_ID: volumeConfiguration.volume.id } : {}
|
|
981
968
|
},
|
|
@@ -994,6 +981,84 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
994
981
|
} : null
|
|
995
982
|
};
|
|
996
983
|
}
|
|
984
|
+
async function ensureRailwayServiceVolumeWithCliFallback({
|
|
985
|
+
tenantRoot,
|
|
986
|
+
projectId,
|
|
987
|
+
environmentId,
|
|
988
|
+
environmentName,
|
|
989
|
+
serviceId,
|
|
990
|
+
serviceName,
|
|
991
|
+
name,
|
|
992
|
+
mountPath,
|
|
993
|
+
env = process.env
|
|
994
|
+
}) {
|
|
995
|
+
try {
|
|
996
|
+
return await ensureRailwayServiceVolume({
|
|
997
|
+
projectId,
|
|
998
|
+
environmentId,
|
|
999
|
+
serviceId,
|
|
1000
|
+
name,
|
|
1001
|
+
mountPath,
|
|
1002
|
+
env
|
|
1003
|
+
});
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1006
|
+
if (!message.includes("Problem processing request")) {
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const cliOptions = {
|
|
1011
|
+
cwd: tenantRoot,
|
|
1012
|
+
capture: true,
|
|
1013
|
+
env
|
|
1014
|
+
};
|
|
1015
|
+
const volumeArgs = ["volume", "--service", serviceId, "--environment", environmentId];
|
|
1016
|
+
const listResult = runRailway([...volumeArgs, "list", "--json"], cliOptions);
|
|
1017
|
+
const existingVolumes = normalizeRailwayCliVolumeList(parseRailwayJsonOutput(listResult.stdout ?? ""), {
|
|
1018
|
+
serviceId,
|
|
1019
|
+
environmentId,
|
|
1020
|
+
fallbackName: name,
|
|
1021
|
+
fallbackMountPath: mountPath
|
|
1022
|
+
});
|
|
1023
|
+
let volume = existingVolumes.find((entry) => entry.name === name) ?? existingVolumes.find((entry) => entry.instances.some((instance2) => instance2.mountPath === mountPath)) ?? existingVolumes[0] ?? null;
|
|
1024
|
+
let created = false;
|
|
1025
|
+
let updated = false;
|
|
1026
|
+
if (!volume) {
|
|
1027
|
+
const createResult = runRailway([...volumeArgs, "add", "--mount-path", mountPath, "--json"], cliOptions);
|
|
1028
|
+
volume = normalizeRailwayCliVolume(parseRailwayJsonOutput(createResult.stdout ?? ""), {
|
|
1029
|
+
serviceId,
|
|
1030
|
+
environmentId,
|
|
1031
|
+
fallbackName: name,
|
|
1032
|
+
fallbackMountPath: mountPath
|
|
1033
|
+
});
|
|
1034
|
+
if (!volume) {
|
|
1035
|
+
throw new Error(`Railway CLI volume add did not return a usable volume for ${serviceName} in ${environmentName}.`);
|
|
1036
|
+
}
|
|
1037
|
+
created = true;
|
|
1038
|
+
}
|
|
1039
|
+
const instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null;
|
|
1040
|
+
if (volume.name !== name || instance?.mountPath !== mountPath) {
|
|
1041
|
+
const updateResult = runRailway([...volumeArgs, "update", "--volume", volume.id, "--name", name, "--mount-path", mountPath, "--json"], cliOptions);
|
|
1042
|
+
const updatedVolume = normalizeRailwayCliVolume(parseRailwayJsonOutput(updateResult.stdout ?? ""), {
|
|
1043
|
+
serviceId,
|
|
1044
|
+
environmentId,
|
|
1045
|
+
fallbackName: name,
|
|
1046
|
+
fallbackMountPath: mountPath
|
|
1047
|
+
});
|
|
1048
|
+
volume = updatedVolume ?? {
|
|
1049
|
+
...volume,
|
|
1050
|
+
name,
|
|
1051
|
+
instances: volume.instances.map((entry) => ({ ...entry, mountPath }))
|
|
1052
|
+
};
|
|
1053
|
+
updated = true;
|
|
1054
|
+
}
|
|
1055
|
+
return {
|
|
1056
|
+
volume,
|
|
1057
|
+
instance: volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null,
|
|
1058
|
+
created,
|
|
1059
|
+
updated
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
997
1062
|
async function deployRailwayService(tenantRoot, service, {
|
|
998
1063
|
dryRun = false,
|
|
999
1064
|
write,
|