@tachyon-gg/railway-deploy 0.2.3 → 0.2.5
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/README.md +18 -2
- package/dist/index.js +157 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,11 +209,26 @@ variables:
|
|
|
209
209
|
| `${ENV_VAR}` | At config load time | Reads from local environment (or `--env-file`) |
|
|
210
210
|
| `${{service.VAR}}` | At Railway runtime | Railway reference variable (cross-service) |
|
|
211
211
|
| `%{param}` | At config load time | Template parameter substitution |
|
|
212
|
+
| `%{service_name}` | At config load time | Built-in: the service's config key |
|
|
212
213
|
| `null` | N/A | Marks a variable for deletion |
|
|
213
214
|
|
|
215
|
+
`%{param}` is expanded first, so it can be used inside `${{}}` Railway references. This is useful for templates that need to reference their own or other services' variables:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
variables:
|
|
219
|
+
# Reference own service's variable (resolves %{service_name} at config time,
|
|
220
|
+
# Railway resolves the ${{}} reference at runtime)
|
|
221
|
+
DATABASE_URL: ${{%{service_name}.DATABASE_URL}}
|
|
222
|
+
|
|
223
|
+
# Reference another service by param
|
|
224
|
+
REDIS_URL: ${{%{cache_service}.REDIS_URL}}
|
|
225
|
+
```
|
|
226
|
+
|
|
214
227
|
### Service templates
|
|
215
228
|
|
|
216
|
-
Templates extract reusable service definitions with parameterized values
|
|
229
|
+
Templates extract reusable service definitions with parameterized values.
|
|
230
|
+
|
|
231
|
+
The built-in `%{service_name}` param is always available and resolves to the service's key in the config (e.g., `web`, `api`). It cannot be overridden.
|
|
217
232
|
|
|
218
233
|
```yaml
|
|
219
234
|
# services/web.yaml
|
|
@@ -228,10 +243,11 @@ source:
|
|
|
228
243
|
|
|
229
244
|
variables:
|
|
230
245
|
APP_VERSION: "%{tag}"
|
|
246
|
+
SERVICE_NAME: "%{service_name}"
|
|
231
247
|
DATABASE_URL: ${{Postgres.DATABASE_URL}}
|
|
232
248
|
|
|
233
249
|
domains:
|
|
234
|
-
- "%{
|
|
250
|
+
- "%{service_name}.example.com"
|
|
235
251
|
|
|
236
252
|
healthcheck:
|
|
237
253
|
path: /health
|
package/dist/index.js
CHANGED
|
@@ -23805,6 +23805,7 @@ var {
|
|
|
23805
23805
|
|
|
23806
23806
|
// src/index.ts
|
|
23807
23807
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
23808
|
+
import { createRequire as createRequire2 } from "module";
|
|
23808
23809
|
import { resolve as resolve2 } from "path";
|
|
23809
23810
|
import { createInterface } from "readline";
|
|
23810
23811
|
|
|
@@ -37708,9 +37709,12 @@ function resolveService(name, entry, envDir, lenient = false) {
|
|
|
37708
37709
|
validateServiceTemplate(parsed, templatePath);
|
|
37709
37710
|
template = parsed;
|
|
37710
37711
|
}
|
|
37711
|
-
|
|
37712
|
+
if ("service_name" in (entry.params ?? {}) || "service_name" in (template?.params ?? {})) {
|
|
37713
|
+
throw new Error(`"service_name" is a built-in parameter and cannot be overridden (service "${name}")`);
|
|
37714
|
+
}
|
|
37715
|
+
let params = { service_name: name };
|
|
37712
37716
|
if (template?.params) {
|
|
37713
|
-
params = resolveParams(template.params, entry.params || {});
|
|
37717
|
+
params = { ...resolveParams(template.params, entry.params || {}), service_name: name };
|
|
37714
37718
|
}
|
|
37715
37719
|
let source = template?.source ? expandParamsDeep(template.source, params) : entry.source;
|
|
37716
37720
|
const volume = template?.volume ? expandParamsDeep(template.volume, params) : entry.volume;
|
|
@@ -37739,9 +37743,8 @@ function resolveService(name, entry, envDir, lenient = false) {
|
|
|
37739
37743
|
const railwayConfigFile = template?.railway_config_file ? expandParamsDeep(template.railway_config_file, params) : entry.railway_config_file;
|
|
37740
37744
|
const staticOutboundIps = template?.static_outbound_ips ?? entry.static_outbound_ips;
|
|
37741
37745
|
let templateDomains = [];
|
|
37742
|
-
if (template) {
|
|
37743
|
-
|
|
37744
|
-
templateDomains = normalizeDomains(tplDomains);
|
|
37746
|
+
if (template?.domains) {
|
|
37747
|
+
templateDomains = normalizeDomains(expandParamsDeep(template.domains, params));
|
|
37745
37748
|
}
|
|
37746
37749
|
const entryDomains = normalizeDomains(entry.domains);
|
|
37747
37750
|
if (entry.source)
|
|
@@ -38446,8 +38449,8 @@ var EgressGatewayAssociationCreateDocument = { kind: "Document", definitions: [{
|
|
|
38446
38449
|
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" } } }] }] } }] };
|
|
38447
38450
|
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" } }] } }] } }] } }] } }] };
|
|
38448
38451
|
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" } }] } }] } }] } }] } }] } }] } }] } }] } }] };
|
|
38449
|
-
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" } } }] }] } }] };
|
|
38450
|
-
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" } } }] }] } }] };
|
|
38452
|
+
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 } }] }] } }] };
|
|
38453
|
+
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 } }] }] } }] };
|
|
38451
38454
|
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" } }] } }] } }] };
|
|
38452
38455
|
var GetServiceInstanceLimitsDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetServiceInstanceLimits" }, 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: "serviceInstanceLimits" }, 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" } } }] }] } }] };
|
|
38453
38456
|
var GetEgressGatewaysDocument = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "GetEgressGateways" }, 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: "egressGateways" }, 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: "ipv4" } }, { kind: "Field", name: { kind: "Name", value: "region" } }] } }] } }] };
|
|
@@ -38890,7 +38893,19 @@ function yellow(text, noColor) {
|
|
|
38890
38893
|
function dim(text, noColor) {
|
|
38891
38894
|
return noColor ? text : `\x1B[2m${text}\x1B[0m`;
|
|
38892
38895
|
}
|
|
38893
|
-
var SENSITIVE_PATTERNS = [
|
|
38896
|
+
var SENSITIVE_PATTERNS = [
|
|
38897
|
+
"PASSWORD",
|
|
38898
|
+
"PASSPHRASE",
|
|
38899
|
+
"SECRET",
|
|
38900
|
+
"TOKEN",
|
|
38901
|
+
"KEY",
|
|
38902
|
+
"PRIVATE",
|
|
38903
|
+
"CREDENTIAL",
|
|
38904
|
+
"JWT",
|
|
38905
|
+
"CERT",
|
|
38906
|
+
"SIGNING",
|
|
38907
|
+
"ENCRYPTION"
|
|
38908
|
+
];
|
|
38894
38909
|
function isSensitive(key) {
|
|
38895
38910
|
const upper = key.toUpperCase();
|
|
38896
38911
|
return SENSITIVE_PATTERNS.some((p) => upper.includes(p));
|
|
@@ -38910,22 +38925,25 @@ function describeChange(change) {
|
|
|
38910
38925
|
if (change.cronSchedule)
|
|
38911
38926
|
details.push(`cron: ${change.cronSchedule}`);
|
|
38912
38927
|
return {
|
|
38913
|
-
category: "
|
|
38928
|
+
category: "Service",
|
|
38914
38929
|
action: "create",
|
|
38915
|
-
|
|
38930
|
+
serviceName: change.name,
|
|
38931
|
+
summary: `create (${details.join(", ")})`
|
|
38916
38932
|
};
|
|
38917
38933
|
}
|
|
38918
38934
|
case "delete-service":
|
|
38919
38935
|
return {
|
|
38920
|
-
category: "
|
|
38936
|
+
category: "Service",
|
|
38921
38937
|
action: "delete",
|
|
38922
|
-
|
|
38938
|
+
serviceName: change.name,
|
|
38939
|
+
summary: `delete (${change.serviceId})`
|
|
38923
38940
|
};
|
|
38924
38941
|
case "update-service-settings":
|
|
38925
38942
|
return {
|
|
38926
|
-
category: "
|
|
38943
|
+
category: "Settings",
|
|
38927
38944
|
action: "update",
|
|
38928
|
-
|
|
38945
|
+
serviceName: change.serviceName,
|
|
38946
|
+
summary: `settings: ${Object.keys(change.settings).join(", ")}`
|
|
38929
38947
|
};
|
|
38930
38948
|
case "update-deployment-trigger": {
|
|
38931
38949
|
const parts = [];
|
|
@@ -38934,82 +38952,99 @@ function describeChange(change) {
|
|
|
38934
38952
|
if (change.checkSuites !== undefined)
|
|
38935
38953
|
parts.push(`checkSuites → ${change.checkSuites}`);
|
|
38936
38954
|
return {
|
|
38937
|
-
category: "
|
|
38955
|
+
category: "Trigger",
|
|
38938
38956
|
action: "update",
|
|
38939
|
-
|
|
38957
|
+
serviceName: change.serviceName,
|
|
38958
|
+
summary: `trigger: ${parts.join(", ")}`
|
|
38940
38959
|
};
|
|
38941
38960
|
}
|
|
38942
38961
|
case "upsert-variables":
|
|
38943
38962
|
return {
|
|
38944
|
-
category: "
|
|
38963
|
+
category: "Variables",
|
|
38945
38964
|
action: "update",
|
|
38946
|
-
|
|
38965
|
+
serviceName: change.serviceName,
|
|
38966
|
+
summary: `set ${Object.keys(change.variables).length} var(s) — ${Object.keys(change.variables).join(", ")}`
|
|
38947
38967
|
};
|
|
38948
38968
|
case "delete-variables":
|
|
38949
38969
|
return {
|
|
38950
|
-
category: "
|
|
38970
|
+
category: "Variables",
|
|
38951
38971
|
action: "delete",
|
|
38952
|
-
|
|
38972
|
+
serviceName: change.serviceName,
|
|
38973
|
+
summary: `delete ${change.variableNames.length} var(s) — ${change.variableNames.join(", ")}`
|
|
38953
38974
|
};
|
|
38954
38975
|
case "upsert-shared-variables":
|
|
38955
38976
|
return {
|
|
38956
38977
|
category: "Shared variables",
|
|
38957
38978
|
action: "update",
|
|
38979
|
+
serviceName: null,
|
|
38958
38980
|
summary: `set ${Object.keys(change.variables).length} var(s) — ${Object.keys(change.variables).join(", ")}`
|
|
38959
38981
|
};
|
|
38960
38982
|
case "delete-shared-variables":
|
|
38961
38983
|
return {
|
|
38962
38984
|
category: "Shared variables",
|
|
38963
38985
|
action: "delete",
|
|
38986
|
+
serviceName: null,
|
|
38964
38987
|
summary: `delete ${change.variableNames.length} var(s) — ${change.variableNames.join(", ")}`
|
|
38965
38988
|
};
|
|
38966
38989
|
case "create-domain": {
|
|
38967
38990
|
const port = change.targetPort ? ` (port ${change.targetPort})` : "";
|
|
38968
38991
|
return {
|
|
38969
|
-
category: "
|
|
38992
|
+
category: "Domain",
|
|
38970
38993
|
action: "create",
|
|
38971
|
-
|
|
38994
|
+
serviceName: change.serviceName,
|
|
38995
|
+
summary: `domain: ${change.domain}${port}`
|
|
38972
38996
|
};
|
|
38973
38997
|
}
|
|
38974
38998
|
case "delete-domain":
|
|
38975
38999
|
return {
|
|
38976
|
-
category: "
|
|
39000
|
+
category: "Domain",
|
|
38977
39001
|
action: "delete",
|
|
38978
|
-
|
|
39002
|
+
serviceName: change.serviceName,
|
|
39003
|
+
summary: `domain: ${change.domain}`
|
|
38979
39004
|
};
|
|
38980
39005
|
case "create-service-domain": {
|
|
38981
39006
|
const port = change.targetPort ? ` (port ${change.targetPort})` : "";
|
|
38982
39007
|
return {
|
|
38983
|
-
category: "Railway
|
|
39008
|
+
category: "Railway domain",
|
|
38984
39009
|
action: "create",
|
|
38985
|
-
|
|
39010
|
+
serviceName: change.serviceName,
|
|
39011
|
+
summary: `railway domain${port}`
|
|
38986
39012
|
};
|
|
38987
39013
|
}
|
|
38988
39014
|
case "delete-service-domain":
|
|
38989
|
-
return {
|
|
39015
|
+
return {
|
|
39016
|
+
category: "Railway domain",
|
|
39017
|
+
action: "delete",
|
|
39018
|
+
serviceName: change.serviceName,
|
|
39019
|
+
summary: "railway domain"
|
|
39020
|
+
};
|
|
38990
39021
|
case "create-volume":
|
|
38991
39022
|
return {
|
|
38992
|
-
category: "
|
|
39023
|
+
category: "Volume",
|
|
38993
39024
|
action: "create",
|
|
38994
|
-
|
|
39025
|
+
serviceName: change.serviceName,
|
|
39026
|
+
summary: `volume: ${change.mount}`
|
|
38995
39027
|
};
|
|
38996
39028
|
case "delete-volume":
|
|
38997
39029
|
return {
|
|
38998
|
-
category: "
|
|
39030
|
+
category: "Volume",
|
|
38999
39031
|
action: "delete",
|
|
39000
|
-
|
|
39032
|
+
serviceName: change.serviceName,
|
|
39033
|
+
summary: `volume (${change.volumeId})`
|
|
39001
39034
|
};
|
|
39002
39035
|
case "create-tcp-proxy":
|
|
39003
39036
|
return {
|
|
39004
|
-
category: "TCP
|
|
39037
|
+
category: "TCP proxy",
|
|
39005
39038
|
action: "create",
|
|
39006
|
-
|
|
39039
|
+
serviceName: change.serviceName,
|
|
39040
|
+
summary: `tcp proxy: port ${change.applicationPort}`
|
|
39007
39041
|
};
|
|
39008
39042
|
case "delete-tcp-proxy":
|
|
39009
39043
|
return {
|
|
39010
|
-
category: "TCP
|
|
39044
|
+
category: "TCP proxy",
|
|
39011
39045
|
action: "delete",
|
|
39012
|
-
|
|
39046
|
+
serviceName: change.serviceName,
|
|
39047
|
+
summary: `tcp proxy: ${change.proxyId}`
|
|
39013
39048
|
};
|
|
39014
39049
|
case "update-service-limits": {
|
|
39015
39050
|
const parts = [];
|
|
@@ -39018,36 +39053,50 @@ function describeChange(change) {
|
|
|
39018
39053
|
if (change.limits.vCPUs !== undefined)
|
|
39019
39054
|
parts.push(`vCPUs: ${change.limits.vCPUs ?? "unset"}`);
|
|
39020
39055
|
return {
|
|
39021
|
-
category: "
|
|
39056
|
+
category: "Limits",
|
|
39022
39057
|
action: "update",
|
|
39023
|
-
|
|
39058
|
+
serviceName: change.serviceName,
|
|
39059
|
+
summary: `limits: ${parts.join(", ")}`
|
|
39024
39060
|
};
|
|
39025
39061
|
}
|
|
39026
39062
|
case "enable-static-ips":
|
|
39027
39063
|
return {
|
|
39028
|
-
category: "Static
|
|
39064
|
+
category: "Static IPs",
|
|
39029
39065
|
action: "create",
|
|
39030
|
-
|
|
39066
|
+
serviceName: change.serviceName,
|
|
39067
|
+
summary: "static outbound IPs: enable"
|
|
39031
39068
|
};
|
|
39032
39069
|
case "disable-static-ips":
|
|
39033
39070
|
return {
|
|
39034
|
-
category: "Static
|
|
39071
|
+
category: "Static IPs",
|
|
39035
39072
|
action: "delete",
|
|
39036
|
-
|
|
39073
|
+
serviceName: change.serviceName,
|
|
39074
|
+
summary: "static outbound IPs: disable"
|
|
39037
39075
|
};
|
|
39038
39076
|
case "create-bucket":
|
|
39039
|
-
return {
|
|
39077
|
+
return {
|
|
39078
|
+
category: "Buckets",
|
|
39079
|
+
action: "create",
|
|
39080
|
+
serviceName: null,
|
|
39081
|
+
summary: change.bucketName
|
|
39082
|
+
};
|
|
39040
39083
|
case "delete-bucket":
|
|
39041
|
-
return { category: "Buckets", action: "delete", summary: change.name };
|
|
39084
|
+
return { category: "Buckets", action: "delete", serviceName: null, summary: change.name };
|
|
39042
39085
|
default: {
|
|
39043
39086
|
const _exhaustive = change;
|
|
39044
|
-
return {
|
|
39087
|
+
return {
|
|
39088
|
+
category: "Unknown",
|
|
39089
|
+
action: "update",
|
|
39090
|
+
serviceName: null,
|
|
39091
|
+
summary: _exhaustive.type
|
|
39092
|
+
};
|
|
39045
39093
|
}
|
|
39046
39094
|
}
|
|
39047
39095
|
}
|
|
39048
39096
|
function changeLabel(change) {
|
|
39049
39097
|
const desc = describeChange(change);
|
|
39050
|
-
|
|
39098
|
+
const prefix = desc.serviceName ? `${desc.serviceName}: ` : "";
|
|
39099
|
+
return `${prefix}${desc.summary}`;
|
|
39051
39100
|
}
|
|
39052
39101
|
var ACTION_ICON = {
|
|
39053
39102
|
create: (nc) => green("+", nc),
|
|
@@ -39066,44 +39115,60 @@ ${green("No changes needed", noColor)} — Railway matches desired state.
|
|
|
39066
39115
|
console.log(`
|
|
39067
39116
|
Changeset (${changeset.changes.length} changes):
|
|
39068
39117
|
`);
|
|
39069
|
-
const
|
|
39070
|
-
|
|
39071
|
-
|
|
39072
|
-
|
|
39118
|
+
const described = changeset.changes.map((change) => ({ change, desc: describeChange(change) }));
|
|
39119
|
+
const serviceGroups = new Map;
|
|
39120
|
+
for (const entry of described) {
|
|
39121
|
+
const key = entry.desc.serviceName;
|
|
39122
|
+
let group = serviceGroups.get(key);
|
|
39073
39123
|
if (!group) {
|
|
39074
39124
|
group = [];
|
|
39075
|
-
|
|
39125
|
+
serviceGroups.set(key, group);
|
|
39126
|
+
}
|
|
39127
|
+
group.push(entry);
|
|
39128
|
+
}
|
|
39129
|
+
const projectChanges = serviceGroups.get(null);
|
|
39130
|
+
if (projectChanges) {
|
|
39131
|
+
for (const { change, desc } of projectChanges) {
|
|
39132
|
+
const icon = ACTION_ICON[desc.action](noColor);
|
|
39133
|
+
if (verbose && change.type === "upsert-shared-variables") {
|
|
39134
|
+
console.log(` ${yellow("~", noColor)} Shared variables:`);
|
|
39135
|
+
for (const [key, value] of Object.entries(change.variables)) {
|
|
39136
|
+
const oldVal = options?.currentState?.sharedVariables[key];
|
|
39137
|
+
const oldStr = oldVal !== undefined ? maskValue(key, oldVal) : "(unset)";
|
|
39138
|
+
console.log(` ${icon} ${key}: ${dim(`"${oldStr}"`, noColor)} → ${dim(`"${maskValue(key, value)}"`, noColor)}`);
|
|
39139
|
+
}
|
|
39140
|
+
} else {
|
|
39141
|
+
console.log(` ${icon} ${desc.category}: ${desc.summary}`);
|
|
39142
|
+
}
|
|
39076
39143
|
}
|
|
39077
|
-
|
|
39144
|
+
console.log();
|
|
39145
|
+
serviceGroups.delete(null);
|
|
39078
39146
|
}
|
|
39079
|
-
for (const [
|
|
39080
|
-
const
|
|
39081
|
-
const
|
|
39147
|
+
for (const [serviceName, entries] of serviceGroups) {
|
|
39148
|
+
const hasCreate = entries.some((e) => e.desc.action === "create" && e.desc.category === "Service");
|
|
39149
|
+
const hasDelete = entries.some((e) => e.desc.action === "delete" && e.desc.category === "Service");
|
|
39150
|
+
const headerAction = hasCreate ? "create" : hasDelete ? "delete" : "update";
|
|
39082
39151
|
const headerIcon = ACTION_ICON[headerAction](noColor);
|
|
39083
|
-
console.log(` ${headerIcon} ${
|
|
39152
|
+
console.log(` ${headerIcon} ${serviceName}:`);
|
|
39084
39153
|
for (const { change, desc } of entries) {
|
|
39085
39154
|
const icon = ACTION_ICON[desc.action](noColor);
|
|
39086
39155
|
if (verbose && change.type === "update-service-settings") {
|
|
39087
|
-
console.log(` ${icon} ${change.serviceName}:`);
|
|
39088
39156
|
for (const [key, value] of Object.entries(change.settings)) {
|
|
39157
|
+
if (isSensitive(key)) {
|
|
39158
|
+
console.log(` ${icon} ${key}: ***`);
|
|
39159
|
+
continue;
|
|
39160
|
+
}
|
|
39089
39161
|
const currentSvc = options?.currentState?.services[change.serviceName];
|
|
39090
39162
|
const oldVal = currentSvc ? currentSvc[key] : undefined;
|
|
39091
39163
|
const oldStr = oldVal !== undefined ? JSON.stringify(oldVal) : "(unset)";
|
|
39092
39164
|
const newStr = value === null ? "(unset)" : JSON.stringify(value);
|
|
39093
|
-
console.log(`
|
|
39165
|
+
console.log(` ${icon} ${key}: ${oldStr} → ${newStr}`);
|
|
39094
39166
|
}
|
|
39095
39167
|
} else if (verbose && change.type === "upsert-variables") {
|
|
39096
|
-
console.log(` ${change.serviceName}:`);
|
|
39097
39168
|
for (const [key, value] of Object.entries(change.variables)) {
|
|
39098
39169
|
const currentSvc = options?.currentState?.services[change.serviceName];
|
|
39099
39170
|
const oldVal = currentSvc?.variables[key];
|
|
39100
39171
|
const oldStr = oldVal !== undefined ? maskValue(key, oldVal) : "(unset)";
|
|
39101
|
-
console.log(` ${icon} ${key}: ${dim(`"${oldStr}"`, noColor)} → ${dim(`"${maskValue(key, value)}"`, noColor)}`);
|
|
39102
|
-
}
|
|
39103
|
-
} else if (verbose && change.type === "upsert-shared-variables") {
|
|
39104
|
-
for (const [key, value] of Object.entries(change.variables)) {
|
|
39105
|
-
const oldVal = options?.currentState?.sharedVariables[key];
|
|
39106
|
-
const oldStr = oldVal !== undefined ? maskValue(key, oldVal) : "(unset)";
|
|
39107
39172
|
console.log(` ${icon} ${key}: ${dim(`"${oldStr}"`, noColor)} → ${dim(`"${maskValue(key, value)}"`, noColor)}`);
|
|
39108
39173
|
}
|
|
39109
39174
|
} else {
|
|
@@ -39115,15 +39180,13 @@ Changeset (${changeset.changes.length} changes):
|
|
|
39115
39180
|
let createCount = 0;
|
|
39116
39181
|
let updateCount = 0;
|
|
39117
39182
|
let deleteCount = 0;
|
|
39118
|
-
for (const
|
|
39119
|
-
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
39123
|
-
|
|
39124
|
-
|
|
39125
|
-
deleteCount++;
|
|
39126
|
-
}
|
|
39183
|
+
for (const { desc } of described) {
|
|
39184
|
+
if (desc.action === "create")
|
|
39185
|
+
createCount++;
|
|
39186
|
+
else if (desc.action === "update")
|
|
39187
|
+
updateCount++;
|
|
39188
|
+
else
|
|
39189
|
+
deleteCount++;
|
|
39127
39190
|
}
|
|
39128
39191
|
const parts = [];
|
|
39129
39192
|
if (createCount > 0)
|
|
@@ -39630,12 +39693,12 @@ function diffServiceSettings(desired, current, changes) {
|
|
|
39630
39693
|
if (desired.ipv6EgressEnabled !== current.ipv6EgressEnabled) {
|
|
39631
39694
|
settings.ipv6EgressEnabled = desired.ipv6EgressEnabled ?? null;
|
|
39632
39695
|
}
|
|
39633
|
-
if (desired.registryCredentials) {
|
|
39634
|
-
settings.registryCredentials = desired.registryCredentials;
|
|
39635
|
-
}
|
|
39636
39696
|
if (desired.railwayConfigFile !== current.railwayConfigFile) {
|
|
39637
39697
|
settings.railwayConfigFile = desired.railwayConfigFile ?? null;
|
|
39638
39698
|
}
|
|
39699
|
+
if (desired.registryCredentials) {
|
|
39700
|
+
settings.registryCredentials = desired.registryCredentials;
|
|
39701
|
+
}
|
|
39639
39702
|
if (Object.keys(settings).length > 0) {
|
|
39640
39703
|
changes.push({
|
|
39641
39704
|
type: "update-service-settings",
|
|
@@ -39835,6 +39898,8 @@ function diffStaticOutboundIps(serviceName, desired, current, changes) {
|
|
|
39835
39898
|
}
|
|
39836
39899
|
|
|
39837
39900
|
// src/index.ts
|
|
39901
|
+
var require2 = createRequire2(import.meta.url);
|
|
39902
|
+
var { version: version2 } = require2("../package.json");
|
|
39838
39903
|
async function confirm(message) {
|
|
39839
39904
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
39840
39905
|
return new Promise((res) => {
|
|
@@ -39844,7 +39909,21 @@ async function confirm(message) {
|
|
|
39844
39909
|
});
|
|
39845
39910
|
});
|
|
39846
39911
|
}
|
|
39847
|
-
var program2 = new Command().name("railway-deploy").description(
|
|
39912
|
+
var program2 = new Command().name("railway-deploy").description(`Declarative Railway infrastructure management.
|
|
39913
|
+
|
|
39914
|
+
Define your Railway services, variables, domains, volumes, and more in YAML,
|
|
39915
|
+
and railway-deploy will diff against the live state and apply changes.
|
|
39916
|
+
|
|
39917
|
+
Examples:
|
|
39918
|
+
$ railway-deploy environments/production.yaml
|
|
39919
|
+
$ railway-deploy --apply -y environments/staging.yaml
|
|
39920
|
+
$ railway-deploy --validate environments/production.yaml
|
|
39921
|
+
$ railway-deploy --env-file .env --apply environments/production.yaml
|
|
39922
|
+
|
|
39923
|
+
Environment:
|
|
39924
|
+
RAILWAY_TOKEN Railway API token (required for all operations except --validate)
|
|
39925
|
+
|
|
39926
|
+
Docs: https://github.com/tachyon-gg/railway-deploy`).version(version2).argument("<config>", "path to environment YAML config file").option("--apply", "execute changes (default is dry-run)").option("--env-file <path>", "load .env file for ${VAR} resolution").option("--no-color", "disable colored output").option("--validate", "validate config without connecting to Railway").option("-v, --verbose", "show detailed diffs with old and new values").option("-y, --yes", "skip confirmation prompts for destructive operations").action(async (configPath, opts) => {
|
|
39848
39927
|
try {
|
|
39849
39928
|
await run(configPath, opts);
|
|
39850
39929
|
} catch (err) {
|
|
@@ -39906,7 +39985,7 @@ Fetching current state from Railway...`);
|
|
|
39906
39985
|
} = await fetchCurrentState(client, projectId, environmentId);
|
|
39907
39986
|
console.log(`Found ${Object.keys(currentState.services).length} existing service(s)`);
|
|
39908
39987
|
const changeset = computeChangeset(desiredState, currentState, deletedVars, deletedSharedVars, domainMap, volumeMap, serviceDomainMap, tcpProxyMap);
|
|
39909
|
-
const noColor = opts.
|
|
39988
|
+
const noColor = opts.color === false;
|
|
39910
39989
|
const verbose = opts.verbose ?? false;
|
|
39911
39990
|
printChangeset(changeset, {
|
|
39912
39991
|
verbose,
|