@tachyon-gg/railway-deploy 0.2.8 → 0.2.10

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 (3) hide show
  1. package/README.md +5 -2
  2. package/dist/index.js +115 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -122,16 +122,19 @@ registry_credentials: # For private container registries
122
122
  #### Build
123
123
 
124
124
  ```yaml
125
- builder: NIXPACKS # RAILPACK (default), DOCKERFILE, NIXPACKS, HEROKU, PAKETO
125
+ builder: NIXPACKS # RAILPACK (default), NIXPACKS, HEROKU, PAKETO
126
126
  build_command: npm run build # Custom build command
127
- dockerfile_path: Dockerfile.prod # Path to Dockerfile (when builder is DOCKERFILE)
127
+ dockerfile_path: Dockerfile.prod # Path to Dockerfile (uses Railpack with Dockerfile)
128
128
  root_directory: /packages/api # Root directory (monorepo support)
129
129
  watch_patterns: # File patterns that trigger deploys
130
130
  - /packages/api/src/**
131
131
  - /packages/shared/**
132
132
  railway_config_file: railway.toml # Path to railway.json/toml for config-as-code
133
+ metal: true # Enable Railway Metal builds (service-level, see note below)
133
134
  ```
134
135
 
136
+ **Note:** Some settings are **service-level** in Railway (applied globally, not per-environment): `metal`, service creation, and service deletion. If you manage multiple environments for the same project, these settings will affect all environments regardless of which YAML file sets them.
137
+
135
138
  #### Deploy
136
139
 
137
140
  ```yaml
package/dist/index.js CHANGED
@@ -37508,7 +37508,8 @@ var ServiceTemplateSchema = exports_external.object({
37508
37508
  tcp_proxies: exports_external.array(exports_external.number().int().positive()).optional(),
37509
37509
  limits: LimitsConfigSchema,
37510
37510
  railway_config_file: exports_external.string().optional(),
37511
- static_outbound_ips: exports_external.boolean().optional()
37511
+ static_outbound_ips: exports_external.boolean().optional(),
37512
+ metal: exports_external.boolean().optional()
37512
37513
  }).strict();
37513
37514
  var ServiceEntrySchema = exports_external.object({
37514
37515
  template: exports_external.string().optional(),
@@ -37540,7 +37541,8 @@ var ServiceEntrySchema = exports_external.object({
37540
37541
  tcp_proxies: exports_external.array(exports_external.number().int().positive()).optional(),
37541
37542
  limits: LimitsConfigSchema,
37542
37543
  railway_config_file: exports_external.string().optional(),
37543
- static_outbound_ips: exports_external.boolean().optional()
37544
+ static_outbound_ips: exports_external.boolean().optional(),
37545
+ metal: exports_external.boolean().optional()
37544
37546
  }).strict();
37545
37547
  var EnvironmentConfigSchema = exports_external.object({
37546
37548
  project: exports_external.string().min(1, "project name is required"),
@@ -37574,7 +37576,7 @@ ${issues}`);
37574
37576
  }
37575
37577
  }
37576
37578
  var VALID_RESTART_POLICIES = ["ALWAYS", "NEVER", "ON_FAILURE"];
37577
- var VALID_BUILDERS = ["RAILPACK", "DOCKERFILE", "NIXPACKS", "HEROKU", "PAKETO"];
37579
+ var VALID_BUILDERS = ["RAILPACK", "NIXPACKS", "HEROKU", "PAKETO"];
37578
37580
  var CRON_FIELD_PATTERN = /^(\*|[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)(\/[0-9]+)?$/;
37579
37581
  var DOMAIN_PATTERN = /^(\*\.)?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
37580
37582
  function validateResolvedService(name, service) {
@@ -37761,6 +37763,7 @@ function resolveService(name, entry, envDir, lenient = false) {
37761
37763
  const limits = template?.limits ?? entry.limits;
37762
37764
  const railwayConfigFile = template?.railway_config_file ? expandParamsDeep(template.railway_config_file, params) : entry.railway_config_file;
37763
37765
  const staticOutboundIps = template?.static_outbound_ips ?? entry.static_outbound_ips;
37766
+ const metal = template?.metal ?? entry.metal;
37764
37767
  let templateDomains = [];
37765
37768
  if (template?.domains) {
37766
37769
  templateDomains = normalizeDomains(expandParamsDeep(template.domains, params));
@@ -37866,6 +37869,8 @@ function resolveService(name, entry, envDir, lenient = false) {
37866
37869
  service.railwayConfigFile = railwayConfigFile;
37867
37870
  if (staticOutboundIps !== undefined)
37868
37871
  service.staticOutboundIps = staticOutboundIps;
37872
+ if (metal !== undefined)
37873
+ service.metal = metal;
37869
37874
  validateResolvedService(name, service);
37870
37875
  return { service, deleted };
37871
37876
  }
@@ -38440,11 +38445,13 @@ function createClient(token) {
38440
38445
  const baseClient = new GraphQLClient(RAILWAY_API, {
38441
38446
  headers: {
38442
38447
  Authorization: `Bearer ${token}`
38443
- },
38444
- signal: AbortSignal.timeout(30000)
38448
+ }
38445
38449
  });
38446
38450
  const originalRequest = baseClient.request.bind(baseClient);
38447
- baseClient.request = (...args) => withRetry(() => originalRequest(...args));
38451
+ baseClient.request = (...args) => withRetry(() => {
38452
+ baseClient.requestConfig.signal = AbortSignal.timeout(120000);
38453
+ return originalRequest(...args);
38454
+ });
38448
38455
  return baseClient;
38449
38456
  }
38450
38457
 
@@ -38467,8 +38474,10 @@ var ServiceInstanceLimitsUpdateDocument = { kind: "Document", definitions: [{ ki
38467
38474
  var DeploymentTriggerUpdateDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "mutation", name: { kind: "Name", value: "DeploymentTriggerUpdate" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }, { kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "DeploymentTriggerUpdateInput" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "deploymentTriggerUpdate" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "id" }, value: { kind: "Variable", name: { kind: "Name", value: "id" } } }, { kind: "Argument", name: { kind: "Name", value: "input" }, value: { kind: "Variable", name: { kind: "Name", value: "input" } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "branch" } }] } }] } }] };
38468
38475
  var EgressGatewayAssociationCreateDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "mutation", name: { kind: "Name", value: "EgressGatewayAssociationCreate" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "EgressGatewayCreateInput" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "egressGatewayAssociationCreate" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "input" }, value: { kind: "Variable", name: { kind: "Name", value: "input" } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "ipv4" } }, { kind: "Field", name: { kind: "Name", value: "region" } }] } }] } }] };
38469
38476
  var EgressGatewayAssociationsClearDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "mutation", name: { kind: "Name", value: "EgressGatewayAssociationsClear" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "EgressGatewayServiceTargetInput" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "egressGatewayAssociationsClear" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "input" }, value: { kind: "Variable", name: { kind: "Name", value: "input" } } }] }] } }] };
38477
+ var ServiceFeatureFlagAddDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "mutation", name: { kind: "Name", value: "ServiceFeatureFlagAdd" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "ServiceFeatureFlagToggleInput" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "serviceFeatureFlagAdd" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "input" }, value: { kind: "Variable", name: { kind: "Name", value: "input" } } }] }] } }] };
38478
+ var ServiceFeatureFlagRemoveDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "mutation", name: { kind: "Name", value: "ServiceFeatureFlagRemove" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "ServiceFeatureFlagToggleInput" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "serviceFeatureFlagRemove" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "input" }, value: { kind: "Variable", name: { kind: "Name", value: "input" } } }] }] } }] };
38470
38479
  var ListProjectsDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "ListProjects" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "projects" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }] } }] } }] } }] } }] };
38471
- var GetProjectDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetProject" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "project" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "id" }, value: { kind: "Variable", name: { kind: "Name", value: "id" } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "environments" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "deploymentTriggers" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "branch" } }, { kind: "Field", name: { kind: "Name", value: "checkSuites" } }, { kind: "Field", name: { kind: "Name", value: "serviceId" } }, { kind: "Field", name: { kind: "Name", value: "repository" } }] } }] } }] } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "services" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "serviceInstances" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "environmentId" } }, { kind: "Field", name: { kind: "Name", value: "source" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "image" } }, { kind: "Field", name: { kind: "Name", value: "repo" } }] } }, { kind: "Field", name: { kind: "Name", value: "domains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "customDomains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "domain" } }, { kind: "Field", name: { kind: "Name", value: "targetPort" } }] } }, { kind: "Field", name: { kind: "Name", value: "serviceDomains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "domain" } }, { kind: "Field", name: { kind: "Name", value: "targetPort" } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "region" } }, { kind: "Field", name: { kind: "Name", value: "numReplicas" } }, { kind: "Field", name: { kind: "Name", value: "restartPolicyType" } }, { kind: "Field", name: { kind: "Name", value: "restartPolicyMaxRetries" } }, { kind: "Field", name: { kind: "Name", value: "healthcheckPath" } }, { kind: "Field", name: { kind: "Name", value: "healthcheckTimeout" } }, { kind: "Field", name: { kind: "Name", value: "cronSchedule" } }, { kind: "Field", name: { kind: "Name", value: "startCommand" } }, { kind: "Field", name: { kind: "Name", value: "buildCommand" } }, { kind: "Field", name: { kind: "Name", value: "rootDirectory" } }, { kind: "Field", name: { kind: "Name", value: "dockerfilePath" } }, { kind: "Field", name: { kind: "Name", value: "preDeployCommand" } }, { kind: "Field", name: { kind: "Name", value: "sleepApplication" } }, { kind: "Field", name: { kind: "Name", value: "builder" } }, { kind: "Field", name: { kind: "Name", value: "watchPatterns" } }, { kind: "Field", name: { kind: "Name", value: "drainingSeconds" } }, { kind: "Field", name: { kind: "Name", value: "overlapSeconds" } }, { kind: "Field", name: { kind: "Name", value: "ipv6EgressEnabled" } }, { kind: "Field", name: { kind: "Name", value: "railwayConfigFile" } }] } }] } }] } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "buckets" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "volumes" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "volumeInstances" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "mountPath" } }, { kind: "Field", name: { kind: "Name", value: "environmentId" } }, { kind: "Field", name: { kind: "Name", value: "serviceId" } }] } }] } }] } }] } }] } }] } }] } }] } }] };
38480
+ var GetProjectDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetProject" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "project" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "id" }, value: { kind: "Variable", name: { kind: "Name", value: "id" } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "environments" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "deploymentTriggers" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "branch" } }, { kind: "Field", name: { kind: "Name", value: "checkSuites" } }, { kind: "Field", name: { kind: "Name", value: "serviceId" } }, { kind: "Field", name: { kind: "Name", value: "repository" } }] } }] } }] } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "services" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "featureFlags" } }, { kind: "Field", name: { kind: "Name", value: "serviceInstances" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "environmentId" } }, { kind: "Field", name: { kind: "Name", value: "source" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "image" } }, { kind: "Field", name: { kind: "Name", value: "repo" } }] } }, { kind: "Field", name: { kind: "Name", value: "domains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "customDomains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "domain" } }, { kind: "Field", name: { kind: "Name", value: "targetPort" } }] } }, { kind: "Field", name: { kind: "Name", value: "serviceDomains" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "domain" } }, { kind: "Field", name: { kind: "Name", value: "targetPort" } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "region" } }, { kind: "Field", name: { kind: "Name", value: "numReplicas" } }, { kind: "Field", name: { kind: "Name", value: "restartPolicyType" } }, { kind: "Field", name: { kind: "Name", value: "restartPolicyMaxRetries" } }, { kind: "Field", name: { kind: "Name", value: "healthcheckPath" } }, { kind: "Field", name: { kind: "Name", value: "healthcheckTimeout" } }, { kind: "Field", name: { kind: "Name", value: "cronSchedule" } }, { kind: "Field", name: { kind: "Name", value: "startCommand" } }, { kind: "Field", name: { kind: "Name", value: "buildCommand" } }, { kind: "Field", name: { kind: "Name", value: "rootDirectory" } }, { kind: "Field", name: { kind: "Name", value: "dockerfilePath" } }, { kind: "Field", name: { kind: "Name", value: "preDeployCommand" } }, { kind: "Field", name: { kind: "Name", value: "sleepApplication" } }, { kind: "Field", name: { kind: "Name", value: "builder" } }, { kind: "Field", name: { kind: "Name", value: "watchPatterns" } }, { kind: "Field", name: { kind: "Name", value: "drainingSeconds" } }, { kind: "Field", name: { kind: "Name", value: "overlapSeconds" } }, { kind: "Field", name: { kind: "Name", value: "ipv6EgressEnabled" } }, { kind: "Field", name: { kind: "Name", value: "railwayConfigFile" } }] } }] } }] } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "buckets" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }] } }] } }] } }, { kind: "Field", name: { kind: "Name", value: "volumes" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", name: { kind: "Name", value: "volumeInstances" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "edges" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "node" }, selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "mountPath" } }, { kind: "Field", name: { kind: "Name", value: "environmentId" } }, { kind: "Field", name: { kind: "Name", value: "serviceId" } }] } }] } }] } }] } }] } }] } }] } }] } }] };
38472
38481
  var GetVariablesDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetVariables" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "projectId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }, { kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "environmentId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }, { kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "serviceId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "variables" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "projectId" }, value: { kind: "Variable", name: { kind: "Name", value: "projectId" } } }, { kind: "Argument", name: { kind: "Name", value: "environmentId" }, value: { kind: "Variable", name: { kind: "Name", value: "environmentId" } } }, { kind: "Argument", name: { kind: "Name", value: "serviceId" }, value: { kind: "Variable", name: { kind: "Name", value: "serviceId" } } }, { kind: "Argument", name: { kind: "Name", value: "unrendered" }, value: { kind: "BooleanValue", value: true } }] }] } }] };
38473
38482
  var GetSharedVariablesDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetSharedVariables" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "projectId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }, { kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "environmentId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "variables" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "projectId" }, value: { kind: "Variable", name: { kind: "Name", value: "projectId" } } }, { kind: "Argument", name: { kind: "Name", value: "environmentId" }, value: { kind: "Variable", name: { kind: "Name", value: "environmentId" } } }, { kind: "Argument", name: { kind: "Name", value: "unrendered" }, value: { kind: "BooleanValue", value: true } }] }] } }] };
38474
38483
  var GetTcpProxiesDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetTcpProxies" }, variableDefinitions: [{ kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "serviceId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }, { kind: "VariableDefinition", variable: { kind: "Variable", name: { kind: "Name", value: "environmentId" } }, type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "String" } } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "tcpProxies" }, arguments: [{ kind: "Argument", name: { kind: "Name", value: "serviceId" }, value: { kind: "Variable", name: { kind: "Name", value: "serviceId" } } }, { kind: "Argument", name: { kind: "Name", value: "environmentId" }, value: { kind: "Variable", name: { kind: "Name", value: "environmentId" } } }], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" } }, { kind: "Field", name: { kind: "Name", value: "applicationPort" } }, { kind: "Field", name: { kind: "Name", value: "proxyPort" } }, { kind: "Field", name: { kind: "Name", value: "domain" } }] } }] } }] };
@@ -38636,6 +38645,9 @@ async function fetchCurrentState(client, projectId, environmentId) {
38636
38645
  if (egressLookup.get(svc.id)) {
38637
38646
  services[svc.name].staticOutboundIps = true;
38638
38647
  }
38648
+ if (svc.featureFlags.includes("USE_VM_RUNTIME")) {
38649
+ services[svc.name].metal = true;
38650
+ }
38639
38651
  if (vol) {
38640
38652
  volumeMap[svc.name] = vol;
38641
38653
  }
@@ -38893,6 +38905,16 @@ async function clearEgressGateways(client, serviceId, environmentId) {
38893
38905
  input: { serviceId, environmentId }
38894
38906
  });
38895
38907
  }
38908
+ async function addServiceFeatureFlag(client, serviceId, flag) {
38909
+ await client.request(ServiceFeatureFlagAddDocument, {
38910
+ input: { serviceId, flag }
38911
+ });
38912
+ }
38913
+ async function removeServiceFeatureFlag(client, serviceId, flag) {
38914
+ await client.request(ServiceFeatureFlagRemoveDocument, {
38915
+ input: { serviceId, flag }
38916
+ });
38917
+ }
38896
38918
  async function createBucket(client, projectId, name) {
38897
38919
  const data = await client.request(BucketCreateDocument, {
38898
38920
  input: { projectId, name }
@@ -39093,6 +39115,24 @@ function describeChange(change) {
39093
39115
  serviceName: change.serviceName,
39094
39116
  summary: "static outbound IPs: disable"
39095
39117
  };
39118
+ case "enable-service-feature-flag": {
39119
+ const flagName = change.flag === "USE_VM_RUNTIME" ? "metal" : change.flag;
39120
+ return {
39121
+ category: "Feature flags",
39122
+ action: "create",
39123
+ serviceName: change.serviceName,
39124
+ summary: `${flagName}: enable`
39125
+ };
39126
+ }
39127
+ case "disable-service-feature-flag": {
39128
+ const flagName = change.flag === "USE_VM_RUNTIME" ? "metal" : change.flag;
39129
+ return {
39130
+ category: "Feature flags",
39131
+ action: "delete",
39132
+ serviceName: change.serviceName,
39133
+ summary: `${flagName}: disable`
39134
+ };
39135
+ }
39096
39136
  case "create-bucket":
39097
39137
  return {
39098
39138
  category: "Buckets",
@@ -39234,6 +39274,25 @@ Apply results:`);
39234
39274
  }
39235
39275
 
39236
39276
  // src/reconcile/apply.ts
39277
+ function extractErrorMessage(err) {
39278
+ if (!(err instanceof Error))
39279
+ return String(err);
39280
+ const msg = err.message;
39281
+ try {
39282
+ const parsed = "response" in err ? err.response : undefined;
39283
+ if (parsed && typeof parsed === "object" && parsed !== null) {
39284
+ const resp = parsed;
39285
+ const gqlMessage = resp.errors?.[0]?.message;
39286
+ if (gqlMessage && gqlMessage !== "Problem processing request") {
39287
+ return gqlMessage;
39288
+ }
39289
+ }
39290
+ } catch {}
39291
+ const match = msg.match(/"message":"([^"]+)"/);
39292
+ if (match)
39293
+ return match[1];
39294
+ return msg;
39295
+ }
39237
39296
  function green2(text, noColor) {
39238
39297
  return noColor ? text : `\x1B[32m${text}\x1B[0m`;
39239
39298
  }
@@ -39263,7 +39322,7 @@ async function applyChangeset(client, changeset, projectId, environmentId, optio
39263
39322
  applied.push(change);
39264
39323
  console.log(` ${green2("✓", noColor)} ${changeLabel(change)}`);
39265
39324
  } catch (err) {
39266
- const message = err instanceof Error ? err.message : String(err);
39325
+ const message = extractErrorMessage(err);
39267
39326
  failed.push({ change, error: message });
39268
39327
  console.log(` ${red2("✗", noColor)} ${changeLabel(change)} — ${message}`);
39269
39328
  }
@@ -39458,6 +39517,22 @@ async function applyChange(client, change, projectId, environmentId, createdServ
39458
39517
  await clearEgressGateways(client, serviceId, environmentId);
39459
39518
  break;
39460
39519
  }
39520
+ case "enable-service-feature-flag": {
39521
+ const serviceId = change.serviceId || createdServiceIds.get(change.serviceName);
39522
+ if (!serviceId) {
39523
+ throw new Error(`No service ID for "${change.serviceName}"`);
39524
+ }
39525
+ await addServiceFeatureFlag(client, serviceId, change.flag);
39526
+ break;
39527
+ }
39528
+ case "disable-service-feature-flag": {
39529
+ const serviceId = change.serviceId || createdServiceIds.get(change.serviceName);
39530
+ if (!serviceId) {
39531
+ throw new Error(`No service ID for "${change.serviceName}"`);
39532
+ }
39533
+ await removeServiceFeatureFlag(client, serviceId, change.flag);
39534
+ break;
39535
+ }
39461
39536
  case "delete-bucket":
39462
39537
  throw new Error("Bucket deletion is not supported by the Railway API — delete manually");
39463
39538
  default: {
@@ -39575,6 +39650,15 @@ function computeChangeset(desired, current, deletedVars, deletedSharedVars, doma
39575
39650
  serviceId: ""
39576
39651
  });
39577
39652
  }
39653
+ if (desiredSvc.metal) {
39654
+ console.warn(` Warning: "${name}" metal flag is service-level — applies across all Railway environments`);
39655
+ changes.push({
39656
+ type: "enable-service-feature-flag",
39657
+ serviceName: name,
39658
+ serviceId: "",
39659
+ flag: "USE_VM_RUNTIME"
39660
+ });
39661
+ }
39578
39662
  if (Object.keys(newSvcSettings).length > 0) {
39579
39663
  changes.push({
39580
39664
  type: "update-service-settings",
@@ -39606,6 +39690,7 @@ function computeChangeset(desired, current, deletedVars, deletedSharedVars, doma
39606
39690
  }
39607
39691
  }
39608
39692
  diffStaticOutboundIps(name, desiredSvc, currentSvc, changes);
39693
+ diffMetal(name, desiredSvc, currentSvc, changes);
39609
39694
  }
39610
39695
  }
39611
39696
  for (const name of currentNames) {
@@ -39915,6 +40000,27 @@ function diffServiceLimits(serviceName, desired, current, changes) {
39915
40000
  });
39916
40001
  }
39917
40002
  }
40003
+ function diffMetal(serviceName, desired, current, changes) {
40004
+ if (!current.id)
40005
+ return;
40006
+ if (desired.metal === true && !current.metal) {
40007
+ console.warn(` Warning: "${serviceName}" metal flag is service-level — applies across all Railway environments`);
40008
+ changes.push({
40009
+ type: "enable-service-feature-flag",
40010
+ serviceName,
40011
+ serviceId: current.id,
40012
+ flag: "USE_VM_RUNTIME"
40013
+ });
40014
+ } else if (desired.metal === false && current.metal) {
40015
+ console.warn(` Warning: "${serviceName}" metal flag is service-level — applies across all Railway environments`);
40016
+ changes.push({
40017
+ type: "disable-service-feature-flag",
40018
+ serviceName,
40019
+ serviceId: current.id,
40020
+ flag: "USE_VM_RUNTIME"
40021
+ });
40022
+ }
40023
+ }
39918
40024
  function diffStaticOutboundIps(serviceName, desired, current, changes) {
39919
40025
  if (!current.id)
39920
40026
  return;
@@ -40033,7 +40139,7 @@ Fetching current state from Railway...`);
40033
40139
  console.log("Nothing to apply.");
40034
40140
  process.exit(0);
40035
40141
  }
40036
- const hasDestructive = changeset.changes.some((c) => c.type === "delete-service" || c.type === "delete-volume" || c.type === "delete-bucket" || c.type === "delete-domain" || c.type === "delete-variables" || c.type === "delete-shared-variables" || c.type === "delete-service-domain" || c.type === "delete-tcp-proxy" || c.type === "disable-static-ips");
40142
+ const hasDestructive = changeset.changes.some((c) => c.type === "delete-service" || c.type === "delete-volume" || c.type === "delete-bucket" || c.type === "delete-domain" || c.type === "delete-variables" || c.type === "delete-shared-variables" || c.type === "delete-service-domain" || c.type === "delete-tcp-proxy" || c.type === "disable-static-ips" || c.type === "disable-service-feature-flag");
40037
40143
  if (hasDestructive && !opts.yes) {
40038
40144
  const ok = await confirm("This changeset includes destructive operations (deletions). Continue?");
40039
40145
  if (!ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tachyon-gg/railway-deploy",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",