Shell scripts on macOS: no /proc, so command lines and memory are asked another way.

macOS and the BSDs have no /proc.

- releases/db-conformance.sh decided whether a container's run was still
  going from /proc/<pid>/cmdline. On a Mac every live run looked dead, so
  a second conformance run removed the first one's containers and
  networks. The command line comes from ps -o command= where there is no
  /proc (tests/dbConformanceStaleContainers.test.cjs passes on macOS now).
- start-wekan.sh and releases/ferretdb/start-wekan.sh sized the Node.js
  and FerretDB heaps from /proc/meminfo, so on a Mac they assumed 2 GiB
  whatever the machine had. They ask sysctl hw.memsize (macOS) or
  hw.physmem (BSD) there, as build.sh already did.
- releases/debug-speed-watch.sh shows the load average from sysctl
  vm.loadavg where there is no /proc/loadavg.

tests/macosShellPortability.test.cjs fails on a /proc memory, load or
command-line read without such a fallback (the Docker entrypoint, which
runs only on Linux, excepted). releases/nextcloud-pack.sh is listed in
tests/buildScriptParity.test.cjs as run by nextcloud.yml.

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu committed 2026-10-27 23:06:49 +02:00
1 parent 0ed8fb20f2
commit 1f9ada5599
6 files changed
+31 -4

No files matched your search

+11 -1
View File
@@ -286,10 +286,20 @@ DB_HOST_PORT_BASE="${WEKAN_CONFORMANCE_DB_PORT:-35432}"
# its queries ("connection refused"), and both of its results were lost.
OWNER_LABEL_KEY="wekan.conformance.pid"
OWNER_LABEL="$OWNER_LABEL_KEY=$$"
# The command line comes from /proc on Linux; macOS and the BSDs have no /proc,
# where every live run looked dead and a second run removed its containers.
# ps -o command= gives the same line there.
command_line_of() {
if [ -r "/proc/$1/cmdline" ]; then
tr '\0' ' ' < "/proc/$1/cmdline" 2>/dev/null
else
ps -o command= -p "$1" 2>/dev/null
fi
}
run_still_going() {
local pid="$1"
[ -n "$pid" ] && [ "$pid" != "$$" ] && kill -0 "$pid" 2>/dev/null &&
tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null | grep -q 'db-conformance\.sh'
command_line_of "$pid" | grep -q 'db-conformance\.sh'
}
left_behind() {
local kind="$1" id owner
+2 -1
View File
@@ -13,7 +13,8 @@ WATCH_PATH="${DEBUGSPEED_WATCH_PATH:-.}"
printf 'timestamp\tload\tmem_available_kb\tdisk_available_kb\n'
while :; do
timestamp="$(date --iso-8601=seconds 2>/dev/null || date)"
load="$(cut -d' ' -f1-3 /proc/loadavg 2>/dev/null || echo unavailable)"
# /proc is Linux; macOS and the BSDs answer sysctl vm.loadavg as "{ 1.2 1.1 1.0 }".
load="$(cut -d' ' -f1-3 /proc/loadavg 2>/dev/null || sysctl -n vm.loadavg 2>/dev/null | tr -d '{}' | awk '{print $1, $2, $3}' || echo unavailable)"
mem_available="$(awk '/^MemAvailable:/{print $2}' /proc/meminfo 2>/dev/null || true)"
disk_available="$(df -Pk "$WATCH_PATH" 2>/dev/null | awk 'NR==2{print $4}')"
printf '%s\t%s\t%s\t%s\n' \
+4 -1
View File
@@ -46,7 +46,10 @@ NODE="$DIR/node"
[ -n "$NODE" ] || { echo "ERROR: no bundled ./node and no node found on PATH" >&2; exit 1; }
# Bound the two bundled runtimes relative to available RAM; explicit overrides win.
_memory_mb=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 2048)
_memory_mb=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null)
# macOS and the BSDs have no /proc; hw.memsize (macOS) and hw.physmem (BSD) are bytes.
[ -n "$_memory_mb" ] || _memory_mb=$(( $(sysctl -n hw.memsize 2>/dev/null || sysctl -n hw.physmem 2>/dev/null || echo 0) / 1048576 ))
[ "$_memory_mb" -gt 0 ] 2>/dev/null || _memory_mb=2048
for _f in /sys/fs/cgroup/memory.max /sys/fs/cgroup/memory/memory.limit_in_bytes; do [ -r "$_f" ] || continue; _b=$(cat "$_f" 2>/dev/null || true); case "$_b" in ''|max|*[!0-9]*) continue;; esac; _m=$((_b/1048576)); [ "$_m" -gt 0 ] && [ "$_m" -lt "$_memory_mb" ] && _memory_mb=$_m; break; done
_heap_mb=$((_memory_mb*3/5)); [ "$_heap_mb" -gt 4096 ] && _heap_mb=4096
# A 32-bit Node has only a 4 GiB virtual address space for the executable,
+4 -1
View File
@@ -927,7 +927,10 @@
#-------------------- OPTIONAL SETTINGS END ----------------------
#bash -c "ulimit -s 65500; exec node --stack-size=65500 --max-old-space-size=8192 main.js"
if [ -z "${NODE_OPTIONS:-}" ]; then
_memory_mb=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 2048)
_memory_mb=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null)
# macOS and the BSDs have no /proc; hw.memsize (macOS) and hw.physmem (BSD) are bytes.
[ -n "$_memory_mb" ] || _memory_mb=$(( $(sysctl -n hw.memsize 2>/dev/null || sysctl -n hw.physmem 2>/dev/null || echo 0) / 1048576 ))
[ "$_memory_mb" -gt 0 ] 2>/dev/null || _memory_mb=2048
for _f in /sys/fs/cgroup/memory.max /sys/fs/cgroup/memory/memory.limit_in_bytes; do [ -r "$_f" ] || continue; _b=$(cat "$_f" 2>/dev/null || true); case "$_b" in ''|max|*[!0-9]*) continue;; esac; _m=$((_b/1048576)); [ "$_m" -gt 0 ] && [ "$_m" -lt "$_memory_mb" ] && _memory_mb=$_m; break; done
_heap_mb=$((_memory_mb*3/5)); [ "$_heap_mb" -gt 4096 ] && _heap_mb=4096
export NODE_OPTIONS="--max-old-space-size=$_heap_mb"
+1
View File
@@ -430,6 +430,7 @@ test('every script in releases/ is reachable from BOTH menus', () => {
// container for musl, Termux-like Android builders, the Bun builders).
SKIP['musl/build-bundle.sh'] = 'musl bundle build run by musl.yml inside Alpine';
SKIP['android/build-bundle.sh'] = 'Android bundle build run by android.yml';
SKIP['nextcloud-pack.sh'] = 'Nextcloud ExApp packer run by nextcloud.yml before signing; tested by tests/nextcloudRelease.test.py';
SKIP['single-file/bun/build-and-check.sh'] = 'Bun single-file build run by bun-single-file.yml';
SKIP['single-file/bun/build.mjs'] = 'Bun single-file builder run by bun-single-file.yml and releases/bun/build-target.sh';
SKIP['single-file/bun/launcher.mjs'] = 'runs INSIDE the Bun single-file executable, embedded by single-file/bun/build.mjs';
+9
View File
@@ -75,6 +75,15 @@ test('sha256sum, nproc and ip only with a macOS fallback', () => {
assert.deepStrictEqual(offenders(c => /^\s*ip (address|addr|a)\b/m.test(c) && !/ifconfig/.test(c)), []);
});
test('/proc memory, load and command lines only with a macOS fallback', () => {
// macOS and the BSDs have no /proc. db-conformance.sh read a run's command
// line there, so on a Mac every live run looked dead and a second run
// removed its containers. The Docker entrypoint only ever runs on Linux.
const linuxOnly = new Set(['releases/ferretdb/wekan-entrypoint.sh']);
assert.deepStrictEqual(offenders(c => /\/proc\/(meminfo|loadavg|\$[A-Za-z_{][^/]*\/cmdline)/.test(c)
&& !/sysctl -n|ps -o command=/.test(c)).filter(rel => !linuxOnly.has(rel)), []);
});
test('no GNU-only ln -T or stat -c', () => {
assert.deepStrictEqual(offenders(c => /\bln -[a-zA-Z]*T\b|\bstat -c\b|\bstat --/.test(c)), []);
});