@stll/oxlint-config 1.2.6 → 1.3.1
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.
|
@@ -532,16 +532,32 @@ const parseEnvFile = (input) => {
|
|
|
532
532
|
if (envFile.startsWith("/") || /^[A-Za-z]:/.test(envFile) || envFile.includes("\\") || /\p{Cc}/u.test(envFile) || envFile.split("/").some((part) => part === "" || part === "." || part === "..")) throw new Error("cloud.envFile must be a canonical repository-relative path");
|
|
533
533
|
return envFile;
|
|
534
534
|
};
|
|
535
|
+
const cloudPrepareScript = /^[a-z0-9][a-z0-9:._-]*$/;
|
|
536
|
+
const parsePrepare = (input) => {
|
|
537
|
+
if (input === void 0) return void 0;
|
|
538
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud.prepare must be an object");
|
|
539
|
+
for (const key of Object.keys(input)) if (key !== "script" && key !== "seconds") throw new Error(`unsupported cloud.prepare key: ${key}`);
|
|
540
|
+
const script = "script" in input ? input.script : void 0;
|
|
541
|
+
if (typeof script !== "string" || !cloudPrepareScript.test(script)) throw new Error("cloud.prepare.script must name a package script");
|
|
542
|
+
const seconds = "seconds" in input ? input.seconds : void 0;
|
|
543
|
+
if (typeof seconds !== "number" || !Number.isInteger(seconds) || seconds < 1 || seconds > 900) throw new Error(`cloud.prepare.seconds must be an integer from 1 to 900`);
|
|
544
|
+
return {
|
|
545
|
+
script,
|
|
546
|
+
seconds
|
|
547
|
+
};
|
|
548
|
+
};
|
|
535
549
|
/** Parse an explicit declaration without assuming services for undeclared repositories. */
|
|
536
550
|
const parseCloudSetup = (input) => {
|
|
537
551
|
if (input === void 0) return void 0;
|
|
538
552
|
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud must be an object");
|
|
539
|
-
for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile" && key !== "rust" && key !== "tools") throw new Error(`unsupported cloud key: ${key}`);
|
|
553
|
+
for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile" && key !== "rust" && key !== "tools" && key !== "prepare") throw new Error(`unsupported cloud key: ${key}`);
|
|
540
554
|
const rust = parseRust("rust" in input ? input.rust : void 0);
|
|
541
555
|
const tools = parseTools("tools" in input ? input.tools : void 0);
|
|
556
|
+
const prepare = parsePrepare("prepare" in input ? input.prepare : void 0);
|
|
542
557
|
const extensions = {
|
|
543
558
|
...rust === void 0 ? {} : { rust },
|
|
544
|
-
...tools === void 0 ? {} : { tools }
|
|
559
|
+
...tools === void 0 ? {} : { tools },
|
|
560
|
+
...prepare === void 0 ? {} : { prepare }
|
|
545
561
|
};
|
|
546
562
|
const selected = "services" in input ? input.services : [];
|
|
547
563
|
if (!Array.isArray(selected)) throw new Error("cloud.services must be an array when provided");
|
|
@@ -596,7 +612,9 @@ service_process_matches() {
|
|
|
596
612
|
owner="$(stat -c '%u' "/proc/$pid" 2>/dev/null)" || return 2
|
|
597
613
|
[[ "$owner" =~ ^[0-9]+$ ]] || return 2
|
|
598
614
|
[[ "$owner" == "$SERVICE_UID" ]] || return 1
|
|
599
|
-
|
|
615
|
+
# Read the executable link as its verified owner: container root may lack the ptrace
|
|
616
|
+
# capability that reading another user's /proc/<pid>/exe requires.
|
|
617
|
+
actual="$(service_run readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
|
|
600
618
|
expected="$(readlink -f "$executable")" || return 2
|
|
601
619
|
[[ -n "$actual" && -n "$expected" && "$actual" != *' (deleted)' ]] || return 2
|
|
602
620
|
[[ "$actual" == "$expected" ]]
|
|
@@ -1068,7 +1086,15 @@ OUTPUT_USER="$(getent passwd "$OUTPUT_UID" | cut -d: -f1)"
|
|
|
1068
1086
|
OUTPUT_HOME="$(getent passwd "$OUTPUT_UID" | cut -d: -f6)"
|
|
1069
1087
|
[[ -n "$OUTPUT_USER" && "$OUTPUT_HOME" == /* && -d "$OUTPUT_HOME" ]] || fail 'Invoking user requires an existing home directory'
|
|
1070
1088
|
root() { "$@"; }
|
|
1071
|
-
|
|
1089
|
+
# Service daemons outlive setup, so they inherit no descriptor beyond stdio: a cloud
|
|
1090
|
+
# runner can pass its terminal as extra descriptors and wait until every copy closes.
|
|
1091
|
+
service_run() (
|
|
1092
|
+
for descriptor in "/proc/$BASHPID/fd/"*; do
|
|
1093
|
+
descriptor="$(basename -- "$descriptor")"
|
|
1094
|
+
if [[ "$descriptor" =~ ^[0-9]+$ ]] && (( descriptor > 2 )); then eval "exec $descriptor>&-"; fi
|
|
1095
|
+
done
|
|
1096
|
+
exec runuser -u stll-cloud -- "$@"
|
|
1097
|
+
)
|
|
1072
1098
|
BUN_VERSION='@BUN@'
|
|
1073
1099
|
NODE_VERSION='@NODE@'
|
|
1074
1100
|
NODE_DIR="/opt/stll-cloud/node/$NODE_VERSION"
|
|
@@ -1171,12 +1197,15 @@ process.stdout.write([...hosts].sort().join(","));
|
|
|
1171
1197
|
|
|
1172
1198
|
@ENV_FUNCTIONS@
|
|
1173
1199
|
|
|
1200
|
+
@PREPARE_FUNCTION@
|
|
1201
|
+
|
|
1174
1202
|
if [[ "$MODE" == install ]]; then
|
|
1175
1203
|
step runtimes install_runtimes
|
|
1176
1204
|
step services install_services
|
|
1177
1205
|
step tools install_tools
|
|
1178
1206
|
step shell install_shell_environment
|
|
1179
1207
|
step dependencies dependency_install
|
|
1208
|
+
@PREPARE_STEP@
|
|
1180
1209
|
exit 0
|
|
1181
1210
|
fi
|
|
1182
1211
|
step runtime-check runtime_ready
|
|
@@ -1217,6 +1246,24 @@ write_environment() {
|
|
|
1217
1246
|
}
|
|
1218
1247
|
`;
|
|
1219
1248
|
const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
1249
|
+
const renderPrepare = (prepare, files) => {
|
|
1250
|
+
if (prepare === void 0) return "";
|
|
1251
|
+
const manifest = files["package.json"];
|
|
1252
|
+
if (manifest === void 0) throw new Error("cloud.prepare requires a tracked root package.json");
|
|
1253
|
+
const parsed = JSON.parse(manifest);
|
|
1254
|
+
const scripts = typeof parsed === "object" && parsed !== null && "scripts" in parsed ? parsed.scripts : void 0;
|
|
1255
|
+
if (typeof scripts !== "object" || scripts === null || !Object.hasOwn(scripts, prepare.script) || typeof Reflect.get(scripts, prepare.script) !== "string") throw new Error(`cloud.prepare.script ${prepare.script} is not a root package script`);
|
|
1256
|
+
return [
|
|
1257
|
+
"prepare_sources() {",
|
|
1258
|
+
" local leader status=0",
|
|
1259
|
+
` timeout --kill-after=10 ${prepare.seconds} runuser -u "$OUTPUT_USER" -- env HOME="$OUTPUT_HOME" PATH="$PATH" "$BUN_DIR/bun" run ${shellQuote(prepare.script)} </dev/null &`,
|
|
1260
|
+
" leader=$!",
|
|
1261
|
+
" wait \"$leader\" || status=$?",
|
|
1262
|
+
" kill -KILL -- \"-$leader\" 2>/dev/null || true",
|
|
1263
|
+
" return \"$status\"",
|
|
1264
|
+
"}"
|
|
1265
|
+
].join("\n");
|
|
1266
|
+
};
|
|
1220
1267
|
const generateCloudSetup = ({ policy, cloud, nodeVersion, files = {}, rustExceptions = [] }) => {
|
|
1221
1268
|
const selected = parseCloudSetup(cloud);
|
|
1222
1269
|
if (selected === void 0) throw new Error("cloud declaration is required");
|
|
@@ -1253,6 +1300,8 @@ const generateCloudSetup = ({ policy, cloud, nodeVersion, files = {}, rustExcept
|
|
|
1253
1300
|
["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
|
|
1254
1301
|
["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
|
|
1255
1302
|
["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
|
|
1303
|
+
["@PREPARE_FUNCTION@", renderPrepare(selected.prepare, files)],
|
|
1304
|
+
["@PREPARE_STEP@", selected.prepare === void 0 ? "" : " step prepare prepare_sources"],
|
|
1256
1305
|
["@SERVICE_INSTALL@", renderCloudServiceInstall({
|
|
1257
1306
|
services,
|
|
1258
1307
|
postgres: policy.postgres,
|