#!/bin/sh
set -u

PREFIX=${PREFIX:-/}
NODE_BIN=${NODE_BIN:-$PREFIX/bin/node}
COREPACK_BIN=${COREPACK_BIN:-$PREFIX/bin/corepack}
ROOT=${ROOT:-/tmp/corepack-berry-install/run}
COREPACK_HOME=${COREPACK_HOME:-/tmp/corepack-berry-install/corepack}
ITERATIONS=${ITERATIONS:-1000}
YARN_VERSION=${YARN_VERSION:-4.17.0}
STACK_TRACE_LIMIT=${STACK_TRACE_LIMIT:-10000}

rm -rf "$ROOT"
mkdir -p "$ROOT/work" "$ROOT/logs" "$COREPACK_HOME"

export PATH="$(dirname "$NODE_BIN"):$PATH"
export HOME="$ROOT/home"
export XDG_CACHE_HOME="$ROOT/xdg-cache"
export XDG_CONFIG_HOME="$ROOT/xdg-config"
export XDG_DATA_HOME="$ROOT/xdg-data"
export COREPACK_HOME="$COREPACK_HOME"
export npm_config_cache="$ROOT/npm-cache"
export YARN_ENABLE_GLOBAL_CACHE=0
export YARN_ENABLE_IMMUTABLE_INSTALLS=0
export YARN_ENABLE_TELEMETRY=0
export YARN_NPM_REGISTRY_SERVER="https://registry.yarnpkg.com"
export NODE_OPTIONS="--stack-trace-limit=$STACK_TRACE_LIMIT"
export CI=true

write_project() {
	rm -rf "$ROOT/work" "$ROOT/home" "$ROOT/xdg-cache" "$ROOT/xdg-config" \
	    "$ROOT/xdg-data" "$ROOT/npm-cache"
	mkdir -p "$ROOT/work" "$ROOT/home" "$ROOT/xdg-cache" "$ROOT/xdg-config" \
	    "$ROOT/xdg-data" "$ROOT/npm-cache"

	cat > "$ROOT/work/package.json" <<JSON
{
  "name": "corepack-berry-install-repro",
  "private": true,
  "packageManager": "yarn@$YARN_VERSION",
  "dependencies": {
    "chai-as-promised": "7.1.2",
    "chalk": "2.4.2",
    "call-bind-apply-helpers": "1.0.2",
    "call-bound": "1.0.4",
    "camelcase": "6.3.0",
    "ccount": "2.0.1",
    "callsites": "3.1.0"
  }
}
JSON
	cat > "$ROOT/work/.yarnrc.yml" <<YAML
cacheFolder: $ROOT/work/.yarn/cache
globalFolder: $ROOT/work/.yarn/global
enableGlobalCache: false
enableMirror: false
compressionLevel: 0
nodeLinker: node-modules
taskPoolMode: workers
taskPoolConcurrency: 1
YAML
}

is_target_stack() {
	rg -q 'RangeError: .*Maximum call stack size exceeded|Maximum call stack size exceeded' "$1"
}

print_target() {
	iter=$1
	log=$2
	echo "FAIL target stack iteration $iter log=$log"
	echo "summary:"
	for pattern in \
	    'Maximum call stack size exceeded' \
	    'memory access out of bounds' \
	    'wasm-function\[104\]' \
	    'wasm-function\[56\]' \
	    'wasm-function\[9\]' \
	    'makeInterface' \
	    'new ZipFS' \
	    'convertToZipWorker'
	do
		printf '  %s=' "$pattern"
		rg -c "$pattern" "$log" || true
	done
	echo "first 120 lines:"
	sed -n '1,120p' "$log"
	echo "last 120 lines:"
	tail -120 "$log"
}

write_project
(cd "$ROOT/work" && "$COREPACK_BIN" "yarn@$YARN_VERSION" --version >/dev/null 2>&1) || true

n=1
non_target_failures=0
while [ "$n" -le "$ITERATIONS" ]; do
	write_project
	log="$ROOT/logs/iter-$n.log"
	{
		printf '[%s] iter=%s node=%s corepack=%s yarn=%s node_options=%s\n' \
		    "$(date +%H:%M:%S)" "$n" "$NODE_BIN" "$COREPACK_BIN" "$YARN_VERSION" "$NODE_OPTIONS"
		"$NODE_BIN" -v
		"$COREPACK_BIN" --version
		(cd "$ROOT/work" && "$COREPACK_BIN" "yarn@$YARN_VERSION" --version)
		(cd "$ROOT/work" && "$COREPACK_BIN" "yarn@$YARN_VERSION" install --mode=skip-build)
	} > "$log" 2>&1
	status=$?

	if is_target_stack "$log"; then
		print_target "$n" "$log"
		exit 1
	fi

	if [ "$status" -ne 0 ]; then
		non_target_failures=$((non_target_failures + 1))
		echo "non-target failure iteration $n status=$status log=$log"
		sed -n '1,120p' "$log"
		if [ "$non_target_failures" -ge 10 ]; then
			echo "too many non-target failures ($non_target_failures), stopping"
			exit "$status"
		fi
	fi

	if [ $((n % 50)) -eq 0 ]; then
		echo "completed $n / $ITERATIONS"
	fi
	n=$((n + 1))
done

echo "OK $ITERATIONS iterations"
