Remove legacy runtime deployment paths

This commit is contained in:
npc0-hue
2026-09-04 12:36:21 +08:00
parent 14cbc63e61
commit 3cfb98ed47
39 changed files with 407 additions and 589 deletions
@@ -1243,21 +1243,6 @@
}
],
"installPlans": [
{
"key": "install-scum-server",
"title": "Install SCUM Dedicated Server",
"platforms": [
"windows"
],
"steps": [
{
"type": "steamcmd-app",
"targetKey": "server/install-root",
"packageManager": "steamcmd",
"packageName": "3792580"
}
]
},
{
"key": "install-steamcmd-linux",
"title": "Install SteamCMD",
@@ -1416,7 +1401,7 @@
"targetKey": "ue4ss/scum-simple-rcon",
"modKey": "scum_simple_rcon",
"dllRef": "ue4ss/Mods/scum_simple_rcon/dlls/main.dll",
"scumExecutableChecksum": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"targetExecutableChecksum": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"ue4ssAbi": "ue4ss-3.0",
"supportedTargets": [
{
@@ -109,12 +109,6 @@
"items": { "$ref": "#/$defs/runtimeInstallPlan" },
"uniqueItems": true
},
"serverDeployments": {
"type": "array",
"items": { "$ref": "#/$defs/runtimeServerDeploymentProfile" },
"uniqueItems": true,
"maxItems": 8
},
"logSources": {
"type": "array",
"items": { "$ref": "#/$defs/runtimeLogSource" },
@@ -557,9 +551,9 @@
"required": ["type", "targetKey"],
"additionalProperties": false,
"properties": {
"type": { "enum": ["package", "verified-download", "steamcmd-app", "manual"] },
"type": { "enum": ["package", "verified-download", "manual"] },
"targetKey": { "$ref": "#/$defs/logicalKey" },
"packageManager": { "enum": ["winget", "choco", "scoop", "apt", "yum", "dnf", "pacman", "zypper", "brew", "steamcmd", "manual"] },
"packageManager": { "enum": ["winget", "choco", "scoop", "apt", "yum", "dnf", "pacman", "zypper", "brew"] },
"packageName": { "type": "string", "pattern": "^[a-zA-Z0-9_.:+@/-]+$", "maxLength": 120 },
"version": { "type": "string", "maxLength": 80 },
"downloadRef": { "type": "string", "pattern": "^https://[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=%-]+$", "maxLength": 240 },
@@ -576,16 +570,6 @@
{
"if": { "properties": { "type": { "const": "verified-download" } }, "required": ["type"] },
"then": { "required": ["downloadRef", "checksum"] }
},
{
"if": { "properties": { "type": { "const": "steamcmd-app" } }, "required": ["type"] },
"then": {
"required": ["packageName"],
"properties": {
"packageManager": { "const": "steamcmd" },
"packageName": { "type": "string", "pattern": "^[0-9]{1,12}$" }
}
}
}
]
},
@@ -600,68 +584,6 @@
"steps": { "type": "array", "items": { "$ref": "#/$defs/runtimeInstallStep" }, "minItems": 1, "maxItems": 64 }
}
},
"runtimeServerConfigMapping": {
"type": "object",
"required": ["fieldKey", "configKey", "valueType"],
"additionalProperties": false,
"properties": {
"fieldKey": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9._/-]*$", "maxLength": 80 },
"configKey": { "$ref": "#/$defs/logicalKey" },
"valueType": { "enum": ["text", "integer", "number", "boolean", "port"] },
"required": { "type": "boolean" }
}
},
"runtimeServerDiscoveryMarker": {
"type": "object",
"required": ["key", "kind", "targetKey"],
"additionalProperties": false,
"properties": {
"key": { "$ref": "#/$defs/logicalKey" },
"kind": { "enum": ["file.exists", "command.version", "port.open", "steam.app"] },
"targetKey": { "$ref": "#/$defs/logicalKey" },
"expected": { "type": "string", "maxLength": 120 },
"required": { "type": "boolean" }
}
},
"runtimeServerVerificationCheck": {
"type": "object",
"required": ["key", "kind", "targetKey"],
"additionalProperties": false,
"properties": {
"key": { "$ref": "#/$defs/logicalKey" },
"kind": { "enum": ["executable.present", "version.matches", "port.bound", "config.readable", "process.healthy"] },
"targetKey": { "$ref": "#/$defs/logicalKey" },
"required": { "type": "boolean" }
}
},
"runtimeServerDeploymentProfile": {
"type": "object",
"required": ["key", "version", "supportedTargets", "steamAppId", "executableKey", "installRootKey", "configKey", "configFormat", "configMappings", "discoveryMarkers", "verificationChecks"],
"additionalProperties": false,
"properties": {
"key": { "$ref": "#/$defs/logicalKey" },
"version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$" },
"supportedTargets": { "type": "array", "items": { "$ref": "#/$defs/runtimeTarget" }, "minItems": 1, "uniqueItems": true },
"steamAppId": { "type": "string", "pattern": "^[0-9]{1,12}$" },
"executableKey": { "$ref": "#/$defs/logicalKey" },
"installRootKey": { "$ref": "#/$defs/logicalKey" },
"configKey": { "$ref": "#/$defs/logicalKey" },
"configFormat": { "enum": ["ini", "json", "yaml", "properties"] },
"prerequisites": { "type": "array", "items": { "$ref": "#/$defs/runtimeServerPrerequisite" }, "maxItems": 16 },
"configMappings": { "type": "array", "items": { "$ref": "#/$defs/runtimeServerConfigMapping" }, "minItems": 1, "maxItems": 32, "uniqueItems": true },
"discoveryMarkers": { "type": "array", "items": { "$ref": "#/$defs/runtimeServerDiscoveryMarker" }, "minItems": 1, "maxItems": 32, "uniqueItems": true },
"verificationChecks": { "type": "array", "items": { "$ref": "#/$defs/runtimeServerVerificationCheck" }, "minItems": 4, "maxItems": 16, "uniqueItems": true }
}
},
"runtimeServerPrerequisite": {
"type": "object",
"required": ["key", "kind"],
"additionalProperties": false,
"properties": {
"key": { "$ref": "#/$defs/logicalKey" },
"kind": { "enum": ["steamcmd", "windows-vcredist", "windows-directx"] }
}
},
"runtimeLogSource": {
"type": "object",
"required": ["key", "kind", "streamKey"],
@@ -724,7 +646,7 @@
"targetKey": { "$ref": "#/$defs/logicalKey" },
"modKey": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,79}$" },
"dllRef": { "type": "string", "pattern": "^ue4ss/Mods/[a-z0-9][a-z0-9_-]{0,79}/dlls/main\\.dll$", "maxLength": 160 },
"scumExecutableChecksum": { "type": "string", "pattern": "^sha256:[a-fA-F0-9]{64}$" },
"targetExecutableChecksum": { "type": "string", "pattern": "^sha256:[a-fA-F0-9]{64}$" },
"ue4ssAbi": { "type": "string", "pattern": "^[A-Za-z0-9._-]{1,80}$" },
"supportedTargets": { "type": "array", "items": { "$ref": "#/$defs/runtimeTarget" }, "minItems": 1, "maxItems": 1, "uniqueItems": true },
"updateOnStart": { "const": true },
@@ -733,7 +655,7 @@
"allOf": [
{
"if": { "properties": { "releaseState": { "const": "ready" } }, "required": ["releaseState"] },
"then": { "required": ["releaseUrl", "checksum", "sizeBytes", "scumExecutableChecksum", "ue4ssAbi"] }
"then": { "required": ["releaseUrl", "checksum", "sizeBytes", "targetExecutableChecksum", "ue4ssAbi"] }
}
]
},
+6 -23
View File
@@ -320,26 +320,9 @@ function validateDependencyPlans(manifest: unknown): string[] {
}
function validateServerDeploymentProfiles(manifest: unknown): string[] {
if (typeof manifest !== "object" || manifest === null || !("server" in manifest)) return [];
const record = manifest as { server?: { createFields?: Array<{ key?: string }> }; runtimeProfiles?: { serverDeployments?: Array<any> } };
const declaredFields = new Set((record.server?.createFields ?? []).map((field) => field.key).filter((key): key is string => Boolean(key)));
const errors: string[] = [];
for (const [index, profile] of (record.runtimeProfiles?.serverDeployments ?? []).entries()) {
const location = `manifest.runtimeProfiles.serverDeployments[${index}]`;
const mappingKeys = new Set<string>();
for (const [mappingIndex, mapping] of (profile.configMappings ?? []).entries()) {
const mappingLocation = `${location}.configMappings[${mappingIndex}]`;
if (!declaredFields.has(mapping.fieldKey)) errors.push(`${mappingLocation}.fieldKey: must reference a declared server.createFields key`);
if (mappingKeys.has(mapping.fieldKey)) errors.push(`${mappingLocation}.fieldKey: duplicate mapping`);
mappingKeys.add(mapping.fieldKey);
}
const requiredChecks = new Set(["executable.present", "port.bound", "config.readable", "process.healthy"]);
for (const check of profile.verificationChecks ?? []) {
if (check.required) requiredChecks.delete(check.kind);
}
for (const missing of requiredChecks) errors.push(`${location}.verificationChecks: required check ${missing} is missing`);
}
return errors;
if (typeof manifest !== "object" || manifest === null || !("server" in manifest)) return [];
const record = manifest as { runtimeProfiles?: { serverDeployments?: unknown } };
return record.runtimeProfiles?.serverDeployments === undefined ? [] : ["manifest.runtimeProfiles.serverDeployments: legacy server deployment profiles are no longer supported"];
}
function validateUnsupportedLegacyClientManagerDeclarations(manifest: unknown): string[] {
@@ -380,7 +363,7 @@ function validateDLLExtensionProfiles(manifest: unknown): string[] {
targetKey?: string;
modKey?: string;
dllRef?: string;
scumExecutableChecksum?: string;
targetExecutableChecksum?: string;
ue4ssAbi?: string;
supportedTargets?: Array<{ os?: string; arch?: string }>;
updateOnStart?: boolean;
@@ -415,8 +398,8 @@ function validateDLLExtensionProfiles(manifest: unknown): string[] {
errors.push(`${location}.releaseState: must be ready or unpublished`);
}
if (profile.releaseState === "ready") {
if (!checksumPattern.test(profile.checksum ?? "") || !checksumPattern.test(profile.scumExecutableChecksum ?? "")) {
errors.push(`${location}: release and SCUM executable SHA-256 checksums are required`);
if (!checksumPattern.test(profile.checksum ?? "") || !checksumPattern.test(profile.targetExecutableChecksum ?? "")) {
errors.push(`${location}: release and target executable SHA-256 checksums are required`);
}
if (!Number.isInteger(profile.sizeBytes) || (profile.sizeBytes ?? 0) < 1 || (profile.sizeBytes ?? 0) > 128 * 1024 * 1024) {
errors.push(`${location}.sizeBytes: must be a bounded DLL size`);
+3 -3
View File
@@ -387,9 +387,9 @@ export interface RuntimeDependencyProbe {
}
export interface RuntimeInstallStep {
type: "package" | "verified-download" | "steamcmd-app" | "manual";
type: "package" | "verified-download" | "manual";
targetKey: string;
packageManager?: "winget" | "choco" | "scoop" | "apt" | "yum" | "dnf" | "pacman" | "zypper" | "brew" | "steamcmd" | "manual";
packageManager?: "winget" | "choco" | "scoop" | "apt" | "yum" | "dnf" | "pacman" | "zypper" | "brew";
packageName?: string;
version?: string;
downloadRef?: string;
@@ -444,7 +444,7 @@ export interface RuntimeDLLExtensionProfile {
targetKey: string;
modKey: string;
dllRef: string;
scumExecutableChecksum?: `sha256:${string}`;
targetExecutableChecksum?: `sha256:${string}`;
ue4ssAbi?: string;
supportedTargets: [{ os: "windows"; arch: "amd64" }];
updateOnStart: true;
@@ -20,6 +20,9 @@
{"key": "unsafe-install", "title": "bash -c installer", "steps": [{"type": "manual", "targetKey": "manual"}]},
{"key": "unsafe-download", "title": "Unsafe dependency download", "steps": [{"type": "verified-download", "targetKey": "tool", "downloadRef": "https://127.0.0.1/tool", "checksum": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}]}
],
"serverDeployments": [
{"key": "legacy", "version": "1.0.0"}
],
"clientManagers": [
{
"key": "unsafe-client",
@@ -165,6 +165,7 @@ describe("plugin manifest validation", () => {
const installScript = fs.readFileSync(path.join(pluginDir, installAction.executableKey), "utf8");
const startScript = fs.readFileSync(path.join(pluginDir, startAction.executableKey), "utf8");
expect(manifest.runtimeProfiles.serverDeployments).toBeUndefined();
expect(JSON.stringify(manifest.runtimeProfiles.installPlans ?? [])).not.toContain("steamcmd-app");
expect(assetPaths).toEqual(expect.arrayContaining(["actions/install.json", "actions/start.json", "bin/scum-install-update.cmd", "bin/scum-start.cmd", "assets/map/scum-map-overview.jpg"]));
expect(installAction).toMatchObject({ executableKey: "bin/scum-install-update.cmd", environment: { SERVER_STEAM_APP_ID: "3792580", SERVER_STEAMCMD_UPDATE_ARGS: "+login anonymous +app_update 3792580 +quit" } });
expect(installAction.timeoutMs).toBe(7200000);
@@ -709,6 +710,7 @@ describe("plugin manifest validation", () => {
expect(errors.some((error) => error.includes("raw host path"))).toBe(true);
expect(errors.some((error) => error.includes("arbitrary shell"))).toBe(true);
expect(errors.some((error) => error.includes("not approved for dependency download"))).toBe(true);
expect(errors.some((error) => error.includes("runtimeProfiles.serverDeployments") && error.includes("no longer supported"))).toBe(true);
expect(errors.some((error) => error.includes("runtimeProfiles.clientManagers") && error.includes("no longer supported"))).toBe(true);
});