@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
- actual="$(readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
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
- service_run() { runuser -u stll-cloud -- "$@" 9>&-; }
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,