Files
helm-charts/satisfactory/values.schema.json
T
jklein 57e54f93fc
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Successful in 3s
Validate Helm Charts / Install-test ${{ fromJSON(needs.detect-changed-charts.outputs.install-charts) }} (pull_request) Skipped
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Successful in 9s
satisfactory: dev and server fall back instead of aborting (7.0.1)
--set dev=null aborted the render at .Values.dev.debug and --set server=null
at .Values.server.replicas, while the third block, game, has fallen back to
the chart defaults since 7.0.0. Half-safe is the worse state of the two:
whoever sees game=null work assumes server=null does too.

Both blocks now run through satisfactory.defaultedDict like game, via two new
helpers next to it. server.resources is deliberately NOT re-defaulted there -
it already runs through satisfactory.effectiveResources, which carries those
defaults and is null-safe on its own, and a second copy of limitFactor: 2 /
4Gi / 1 / 5Gi would be a third place for them to drift.

server is read in four files, not one, and the fourth is NOTES.txt, which
helm template never renders - a fix confined to the manifests would have
looked green and still aborted on a real helm install. All four go through
the helpers.

The single erased key was the quieter half of the bug and is fixed with it:
a nulled server.gamePort rendered "port:" and "nodePort:" empty into the
Service and an empty containerPort into the StatefulSet, and a nulled
server.storage a PVC asking for no size - manifests rejected by nothing until
the API server saw them. server.storage was documented in values.schema.json
as deliberately not null-safe; that reasoning does not survive the block-level
case, because it made "server: null" render a sizeless PVC instead of falling
back like everything else. The schema note is replaced rather than left lying.

Rendered output for valid values is unchanged: helm template against the
chart defaults and against a full override set is byte-identical to 7.0.0,
and so is NOTES.txt. Hence a patch bump, appVersion untouched.

Fixes #16
2026-09-22 19:59:35 +00:00

189 lines
8.4 KiB
JSON

{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "satisfactory values",
"description": "Strict values schema (issue #68). additionalProperties is false on every chart-owned object level, so a key that the chart does not read is rejected at render time instead of being silently ignored. The only objects that stay open are the ones passed verbatim into the Kubernetes pod spec (probes, security contexts, global) - the chart does not interpret their contents, so it cannot judge which keys are meaningful. The hardening, probe and resources blocks additionally accept null, because an erased block falls back to the chart defaults (issue #99); since 7.0.1 that holds for every chart-owned block, dev, game and server included (Gitea issue #16), so no block is half-safe any more. Note that this chart keeps env, probes and securityContext at the TOP level rather than under a chart-named key.",
"type": "object",
"additionalProperties": false,
"properties": {
"global": {
"description": "Helm's conventional cross-chart values. Unused by this chart, kept open so an umbrella install cannot break on it.",
"type": "object"
},
"scaleDown": {
"type": "boolean"
},
"dev": {
"description": "May be null / empty: the chart then falls back to its documented dev defaults (Gitea issue #16, the rest of issue #99) instead of aborting the render with a nil-pointer error, which is what an erased block used to do.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"debug": {
"description": "Passed to the image as DEBUG. Null means the chart default (false).",
"type": ["boolean", "string", "null"]
}
}
},
"game": {
"description": "May be null / empty: the chart then falls back to its documented game defaults (issue #99) instead of aborting the render with a nil-pointer error, which is what an erased block used to do.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"experimental": {
"description": "Passed to the image as STEAMBETA - switches to the experimental game branch.",
"type": ["boolean", "string", "null"]
},
"maxPlayers": {
"type": ["integer", "string", "null"]
},
"enableSeasonalEvents": {
"description": "Seasonal (holiday) events. NEGATED by the chart into the image's DISABLESEASONALEVENTS: true renders DISABLESEASONALEVENTS=\"false\" and the events run, false renders \"true\" and they do not. Only true/false are accepted (boolean or string); anything else aborts the render. Default false, which reproduces what the chart has always rendered - up to 6.1.0 the key was game.seasonalEvents and was passed through unchanged, so its default true wrote DISABLESEASONALEVENTS=true and the events were off despite this description claiming the opposite (issue #215). The old key game.seasonalEvents no longer exists and is rejected here rather than silently ignored.",
"type": ["boolean", "string", "null"]
}
}
},
"server": {
"description": "May be null / empty: the chart then falls back to its documented server defaults (Gitea issue #16, the rest of issue #99) instead of aborting the render with a nil-pointer error, which is what an erased block used to do. server.resources is the exception - it is not defaulted here but passed through to the resources fallback, which carries the defaults for it.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"replicas": {
"description": "Null means the chart default (1). An explicit 0 is kept and scales the StatefulSet down.",
"type": ["integer", "null"],
"minimum": 0
},
"gamePort": {
"description": "Null means the chart default (30777). Up to 7.0.0 an erased port rendered an empty port number into the Service and the StatefulSet, which only the API server rejected (Gitea issue #16).",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"messagingPort": {
"description": "Null means the chart default (30888). Same erased-port trap as gamePort above.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"storage": {
"description": "Size of the data PVC. Null means the chart default (20Gi). Up to 7.0.0 this was deliberately not null-safe, on the reasoning that an erased value should yield an invalid PVC rather than a silently degraded workload; that reasoning did not survive Gitea issue #16, because it made \"server: null\" render a sizeless PVC instead of falling back like every other block. The chart default is not a degradation - it is the documented size.",
"type": ["string", "null"]
},
"resources": {
"$ref": "#/definitions/resources"
}
}
},
"env": {
"$ref": "#/definitions/env"
},
"livenessProbe": {
"$ref": "#/definitions/probe"
},
"readinessProbe": {
"$ref": "#/definitions/probe"
},
"startupProbe": {
"$ref": "#/definitions/probe"
},
"securityContext": {
"$ref": "#/definitions/securityContext"
}
},
"definitions": {
"resources": {
"description": "May be null / empty: the chart then falls back to its default requests and computed limits (issue #99) instead of rendering a container without resource accounting. The default limitFactor is 2 here, not the repo-wide 3, to keep the memory limit at the previously published 8Gi.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"limitFactor": {
"type": "number",
"exclusiveMinimum": 0
},
"requests": {
"$ref": "#/definitions/resourceList"
},
"limits": {
"$ref": "#/definitions/resourceList"
}
}
},
"resourceList": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"cpu": {
"type": ["string", "number", "null"]
},
"memory": {
"type": ["string", "number", "null"]
},
"ephemeral-storage": {
"type": ["string", "number", "null"]
},
"ephemeralStorage": {
"type": ["string", "number", "null"]
}
}
},
"env": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"valueFrom": {
"type": "object",
"additionalProperties": false,
"properties": {
"secretKeyRef": {
"$ref": "#/definitions/keyRef"
},
"configMapKeyRef": {
"$ref": "#/definitions/keyRef"
}
}
}
}
}
},
"keyRef": {
"type": "object",
"additionalProperties": false,
"required": ["name", "key"],
"properties": {
"name": {
"type": "string"
},
"key": {
"type": "string"
}
}
},
"securityContext": {
"description": "May be null / empty at any level: the chart then falls back to its hardening defaults (issue #99) instead of rendering an unhardened workload.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"pod": {
"description": "Merged onto the chart's pod hardening defaults and rendered as the pod's securityContext; kept open because the chart does not interpret the individual fields.",
"type": ["object", "null"]
},
"container": {
"description": "Merged onto the chart's container hardening defaults and rendered as the container's securityContext; kept open because the chart does not interpret the individual fields.",
"type": ["object", "null"]
}
}
},
"probe": {
"description": "Merged onto the chart's probe defaults and rendered as a container probe; kept open because the chart does not interpret the individual fields. May be null / empty, which means the defaults apply (issue #99) rather than the probe disappearing.",
"type": ["object", "null"]
}
}
}