feat(gstack): share the Playwright bump, report the browser cache
Extract gstack_bump_playwright_if_unsupported from install-plugins.sh into lib/gstack-playwright.sh and call it from update-all.sh too. A submodule update no longer leaves the OS-support bump unapplied until the next `make plugin` — that was BDR-029's open caveat. The update helper never touches the submodule working tree: on failure it prints git's own message and points at `make plugin`, and returns non-zero so the existing `else warn` arm still handles it. Add a read-only `Playwright browsers` section to doctor.sh: cache size, which registered install requires each revision, and counts of unreferenced directories and broken links. No pruning is written — Playwright's own `install` already unions the required set across every registered install, and all three installs here are live (rev 1228 for gstack + gsd-pi 1.61, rev 1243 for gsd-pi 1.63). Carries two latent-bug fixes from the moved code: the ostag capture exited 1 on every non-Ubuntu host and aborted the caller under inherited errexit, and the bun calls had no timeout.
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# lib/gstack-playwright.sh — gstack's Playwright: OS-support bump +
|
||||
# read-only browser-cache report.
|
||||
#
|
||||
# Sourced by: install-plugins.sh, update-all.sh, doctor.sh — all three run
|
||||
# `set -euo pipefail`. gstack_bump_playwright_if_unsupported and
|
||||
# gstack_browsers_report are called as BARE STATEMENTS under that inherited
|
||||
# errexit, so they `return 0` on every path and every capture that could
|
||||
# fail is guarded (`|| true` or an `if`), never a bare `&&`/`||`-less
|
||||
# statement. gstack_submodule_update_with_bump is the ONE function allowed
|
||||
# to return non-zero — callers use it ONLY as an `if` condition.
|
||||
#
|
||||
# No `set -euo pipefail` here (mirrors lib/detect-plugins.sh): a sourced
|
||||
# lib must not change the caller's shell options.
|
||||
#
|
||||
# See BDR-029 (bump origin), BLK-008 (Chromium-unsupported-OS saga),
|
||||
# LRN-040 (two-layer fix — this file is layer 1 only).
|
||||
# ============================================================
|
||||
|
||||
_GSPW_GREEN='\033[0;32m'; _GSPW_YELLOW='\033[1;33m'; _GSPW_BLUE='\033[0;34m'
|
||||
_GSPW_NC='\033[0m'
|
||||
|
||||
_gspw_ok() { echo -e " ${_GSPW_GREEN}✓${_GSPW_NC} $1"; }
|
||||
_gspw_warn() { echo -e " ${_GSPW_YELLOW}⚠${_GSPW_NC} $1"; }
|
||||
_gspw_info() { echo -e " ${_GSPW_BLUE}→${_GSPW_NC} $1"; }
|
||||
|
||||
# ── OS support ───────────────────────────────────────────────────────────
|
||||
|
||||
# gstack_pw_ostag [os_release_path] — "ubuntu<VERSION_ID>" on Ubuntu, empty
|
||||
# otherwise. `|| true` on the capture: the reproduced bug had this exact
|
||||
# line abort every non-Ubuntu host under inherited errexit.
|
||||
gstack_pw_ostag() {
|
||||
local path="${1:-/etc/os-release}" tag
|
||||
[ -r "$path" ] || return 0
|
||||
# shellcheck disable=SC1090
|
||||
tag="$(. "$path" 2>/dev/null
|
||||
[ "${ID:-}" = ubuntu ] && printf 'ubuntu%s' "${VERSION_ID:-}")" || true
|
||||
if [ -n "$tag" ]; then
|
||||
printf '%s' "$tag"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# gstack_pw_supports <playwright_core_lib_dir> <ostag> — 0 supported, 1 not.
|
||||
# Always called from an `if`/`&&` context, never as a bare statement.
|
||||
gstack_pw_supports() {
|
||||
local pwlib="$1" ostag="$2"
|
||||
[ -n "$ostag" ] && [ -d "$pwlib" ] || return 1
|
||||
grep -rqs "$ostag" "$pwlib" 2>/dev/null
|
||||
}
|
||||
|
||||
# _gspw_run_timeout <dir> <cmd...> — runs <cmd> in <dir>, under `timeout 300`
|
||||
# when available (absent on stock macOS). Exit 124 = the wrapped command was
|
||||
# killed by the timeout. Callers MUST invoke this via `cmd || rc=$?` (never
|
||||
# bare) so a non-zero exit never trips the caller's inherited errexit.
|
||||
_gspw_run_timeout() {
|
||||
local dir="$1"; shift
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
( cd "$dir" && timeout 300 "$@" ) >/dev/null 2>&1
|
||||
else
|
||||
( cd "$dir" && "$@" ) >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# _gspw_bump_install <gstack_dir> — populate node_modules at the pinned
|
||||
# version so its support list can be read. 0 proceed, 1 give up silently
|
||||
# (both installs failed, matches the pre-existing silent behavior), 2 give
|
||||
# up loud (a timeout truncated node_modules — the support grep would then
|
||||
# read a half-written tree).
|
||||
_gspw_bump_install() {
|
||||
local dir="$1" rc=0
|
||||
_gspw_run_timeout "$dir" bun install --frozen-lockfile || rc=$?
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
return 0
|
||||
elif [ "$rc" -eq 124 ]; then
|
||||
_gspw_warn "bun install timed out — skipping Playwright bump"
|
||||
return 2
|
||||
fi
|
||||
rc=0
|
||||
_gspw_run_timeout "$dir" bun install || rc=$?
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
return 0
|
||||
elif [ "$rc" -eq 124 ]; then
|
||||
_gspw_warn "bun install timed out — skipping Playwright bump"
|
||||
return 2
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# _gspw_bump_add_latest <gstack_dir> — 0 ran (support re-checked by caller
|
||||
# regardless of bun's own exit code, exactly as the pre-existing code did),
|
||||
# 2 timed out (node_modules left half-written — caller must NOT re-check).
|
||||
_gspw_bump_add_latest() {
|
||||
local dir="$1" rc=0
|
||||
_gspw_run_timeout "$dir" bun add playwright@latest || rc=$?
|
||||
if [ "$rc" -eq 124 ]; then
|
||||
_gspw_warn "bun add playwright@latest timed out — skipping Playwright bump"
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# gstack_bump_playwright_if_unsupported <gstack_dir> — BDR-029: bump
|
||||
# gstack's pinned Playwright when it lacks a build for this OS, so
|
||||
# `./setup` rebuilds the browse binary against a version that has one.
|
||||
# OS-gated, idempotent, non-fatal — `return 0` on every path.
|
||||
gstack_bump_playwright_if_unsupported() {
|
||||
local gstack_dir="$1" ostag pwlib rc=0
|
||||
[ -d "$gstack_dir" ] && [ -r /etc/os-release ] || return 0
|
||||
ostag="$(gstack_pw_ostag)"
|
||||
[ -n "$ostag" ] || return 0
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
fi
|
||||
pwlib="$gstack_dir/node_modules/playwright-core/lib"
|
||||
_gspw_info "checking gstack's Playwright OS support ($ostag)..."
|
||||
_gspw_bump_install "$gstack_dir" || rc=$?
|
||||
[ "$rc" -eq 0 ] || return 0
|
||||
if gstack_pw_supports "$pwlib" "$ostag"; then
|
||||
return 0
|
||||
fi
|
||||
_gspw_info "gstack's Playwright lacks $ostag support — bumping to \
|
||||
latest (local submodule edit)..."
|
||||
rc=0
|
||||
_gspw_bump_add_latest "$gstack_dir" || rc=$?
|
||||
[ "$rc" -eq 0 ] || return 0
|
||||
if gstack_pw_supports "$pwlib" "$ostag"; then
|
||||
_gspw_ok "gstack Playwright bumped — now supports $ostag (browse \
|
||||
binary rebuilt by ./setup)"
|
||||
else
|
||||
_gspw_warn "Playwright bump didn't add $ostag support — gstack \
|
||||
browser may stay unavailable"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Submodule update ──────────────────────────────────────────────────────
|
||||
|
||||
# gstack_submodule_update_with_bump <repo> [sub_path] — the ONE function
|
||||
# allowed to return non-zero; callers use it ONLY as an `if` condition.
|
||||
# Never touches the submodule working tree: on failure it prints git's own
|
||||
# stderr verbatim (never parsed) and returns 1. On success it re-applies
|
||||
# the bump (closes BDR-029's caveat: the bump used to survive only until
|
||||
# the next `git submodule update`).
|
||||
gstack_submodule_update_with_bump() {
|
||||
local repo="$1" sub="${2:-skills-external/gstack}" err rc=0
|
||||
err="$(git -C "$repo" submodule update --remote "$sub" 2>&1 >/dev/null)" \
|
||||
|| rc=$?
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
gstack_bump_playwright_if_unsupported "$repo/$sub"
|
||||
return 0
|
||||
fi
|
||||
_gspw_warn "$err"
|
||||
if [ -n "$(git -C "$repo/$sub" status --porcelain \
|
||||
-- package.json bun.lock 2>/dev/null)" ]; then
|
||||
_gspw_info "local Playwright bump (package.json/bun.lock) was not \
|
||||
re-applied — re-run: make plugin"
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Browsers report (read-only) ───────────────────────────────────────────
|
||||
|
||||
# _gspw_dir_name_parts <cache_dir_name> — prints "normalized_name revision"
|
||||
# split on the LAST '-', mapping '_' -> '-' on the name (Playwright writes
|
||||
# chromium_headless_shell-1228 on disk; browsers.json names it
|
||||
# chromium-headless-shell).
|
||||
_gspw_dir_name_parts() {
|
||||
local rev="${1##*-}" name="${1%-*}"
|
||||
printf '%s %s' "${name//_/-}" "$rev"
|
||||
}
|
||||
|
||||
# _gspw_browser_referenced <playwright_core_path> <dir_name> — does that
|
||||
# install require this cache directory (base revision or any
|
||||
# revisionOverrides value)?
|
||||
_gspw_browser_referenced() {
|
||||
local json="$1/browsers.json" name rev
|
||||
[ -r "$json" ] || return 1
|
||||
read -r name rev <<< "$(_gspw_dir_name_parts "$2")"
|
||||
awk -F'"' -v want_name="$name" -v want_rev="$rev" '
|
||||
$2 == "name" { cur = $4; in_ov = 0 }
|
||||
$2 == "revision" && !in_ov && cur == want_name && $4 == want_rev {
|
||||
found = 1
|
||||
}
|
||||
$2 == "revisionOverrides" { in_ov = 1 }
|
||||
in_ov && $2 != "revisionOverrides" && cur == want_name \
|
||||
&& $4 == want_rev { found = 1 }
|
||||
/^[[:space:]]*}/ { in_ov = 0 }
|
||||
END { exit !found }
|
||||
' "$json"
|
||||
}
|
||||
|
||||
# _gspw_browser_name_known <playwright_core_path> <dir_name> — is the NAME
|
||||
# listed at all, regardless of revision? (distinguishes "unknown revision"
|
||||
# from "unreferenced" in the report.)
|
||||
_gspw_browser_name_known() {
|
||||
local json="$1/browsers.json" name rev
|
||||
[ -r "$json" ] || return 1
|
||||
read -r name rev <<< "$(_gspw_dir_name_parts "$2")"
|
||||
awk -F'"' -v want="$name" '$2 == "name" && $4 == want { found = 1 }
|
||||
END { exit !found }' "$json"
|
||||
}
|
||||
|
||||
# _gspw_install_label <playwright_core_path> — "<dir-before-node_modules>
|
||||
# <version>", e.g. "gstack 1.61.1".
|
||||
_gspw_install_label() {
|
||||
local pw_path="$1" parent version
|
||||
parent=$(basename "$(dirname "$(dirname "$pw_path")")")
|
||||
version=$(awk -F'"' '$2 == "version" { print $4; exit }' \
|
||||
"$pw_path/package.json" 2>/dev/null) || true
|
||||
printf '%s %s' "$parent" "${version:-?}"
|
||||
}
|
||||
|
||||
# _gspw_registered_installs <cache_dir> — valid playwright-core paths (dir
|
||||
# exists, browsers.json readable), one per line. A `.links` entry whose
|
||||
# target is gone or unreadable is silently excluded here (it is counted as
|
||||
# a broken link by the caller instead).
|
||||
_gspw_registered_installs() {
|
||||
local links_dir="$1/.links" f target
|
||||
[ -d "$links_dir" ] || return 0
|
||||
for f in "$links_dir"/*; do
|
||||
[ -f "$f" ] || continue
|
||||
target=$(cat "$f" 2>/dev/null) || true
|
||||
[ -n "$target" ] || continue
|
||||
if [ -d "$target" ] && [ -r "$target/browsers.json" ]; then
|
||||
printf '%s\n' "$target"
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# _gspw_report_dir_line <dir_name> <install_paths_newline_sep> — prints the
|
||||
# report line for one cache directory. Returns 1 only when truly
|
||||
# unreferenced (caller tallies that); "unknown revision" does not count.
|
||||
_gspw_report_dir_line() {
|
||||
local dir_name="$1" installs="$2" p labels="" known=0
|
||||
while IFS= read -r p; do
|
||||
[ -n "$p" ] || continue
|
||||
if _gspw_browser_referenced "$p" "$dir_name"; then
|
||||
labels="${labels:+$labels, }$(_gspw_install_label "$p")"
|
||||
elif _gspw_browser_name_known "$p" "$dir_name"; then
|
||||
known=1
|
||||
fi
|
||||
done <<< "$installs"
|
||||
if [ -n "$labels" ]; then
|
||||
_gspw_info "$dir_name: $labels"
|
||||
return 0
|
||||
elif [ "$known" -eq 1 ]; then
|
||||
_gspw_info "$dir_name: unknown revision"
|
||||
return 0
|
||||
fi
|
||||
_gspw_info "$dir_name: unreferenced"
|
||||
return 1
|
||||
}
|
||||
|
||||
# gstack_browsers_report [cache_dir] — read-only. `$1` (or
|
||||
# PLAYWRIGHT_BROWSERS_PATH, or ~/.cache/ms-playwright) is resolved once;
|
||||
# "0" (documented as "bundle into node_modules") and any non-directory
|
||||
# degrade to a silent no-cache path. `return 0` on every path.
|
||||
gstack_browsers_report() {
|
||||
local cache installs total links_total valid_count broken=0 unref=0 d name
|
||||
cache="${1:-${PLAYWRIGHT_BROWSERS_PATH:-$HOME/.cache/ms-playwright}}"
|
||||
[ "$cache" = "0" ] && return 0
|
||||
[ -d "$cache" ] || return 0
|
||||
installs="$(_gspw_registered_installs "$cache")"
|
||||
links_total=$(find "$cache/.links" -maxdepth 1 -type f 2>/dev/null \
|
||||
| wc -l | tr -d ' ') || true
|
||||
valid_count=$(printf '%s\n' "$installs" | grep -c . || true)
|
||||
broken=$((links_total - valid_count))
|
||||
total=$(du -sh "$cache" 2>/dev/null | awk '{print $1}') || true
|
||||
_gspw_info "Playwright browsers: $cache (${total:-0})"
|
||||
for d in "$cache"/*-[0-9]*; do
|
||||
[ -d "$d" ] || continue
|
||||
name=$(basename "$d")
|
||||
_gspw_report_dir_line "$name" "$installs" || unref=$((unref + 1))
|
||||
done
|
||||
_gspw_info "${unref} unreferenced, ${broken} broken link(s)"
|
||||
if [ "$unref" -gt 0 ] || [ "$broken" -gt 0 ]; then
|
||||
_gspw_warn "unreferenced/broken Playwright browser dirs — re-run \
|
||||
\`playwright install\`, which prunes stale revisions"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── CLI dispatch (only when executed, not sourced) — browsers-report ONLY.
|
||||
# The write functions (the bump, the submodule update) stay sourced-only: a
|
||||
# CLI verb would expose `bun add playwright@latest` as a command-line entry
|
||||
# point. ────────────────────────────────────────────────────────────────
|
||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
||||
case "${1:-}" in
|
||||
browsers-report) shift; gstack_browsers_report "$@" ;;
|
||||
*) echo "usage: gstack-playwright.sh browsers-report [cache_dir]" >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env bash
|
||||
# lib/tests/gstack-playwright.test.sh — lib/gstack-playwright.sh (T1..T17)
|
||||
#
|
||||
# git 2.53 defaults protocol.file to "user", which blocks submodule clone
|
||||
# and fetch. The fixture git calls alone are not enough: the
|
||||
# `git submodule update --remote` under test runs INSIDE the lib, in a
|
||||
# fresh git subprocess spawned from THIS process — so the override is
|
||||
# exported for the WHOLE test process, not passed per-command.
|
||||
set -u
|
||||
export GIT_CONFIG_COUNT=1
|
||||
export GIT_CONFIG_KEY_0=protocol.file.allow
|
||||
export GIT_CONFIG_VALUE_0=always
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
L="$ROOT/lib/gstack-playwright.sh"
|
||||
pass=0; fail=0
|
||||
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
|
||||
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
|
||||
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
||||
git_id() { git -C "$1" config user.email t@example.com
|
||||
git -C "$1" config user.name Test; }
|
||||
|
||||
# shellcheck source=lib/gstack-playwright.sh
|
||||
source "$L"
|
||||
|
||||
# ── T1/T2 — ostag detection ──────────────────────────────────────────────
|
||||
printf 'ID=ubuntu\nVERSION_ID="24.04"\n' > "$tmp/os-ubuntu"
|
||||
printf 'ID=debian\nVERSION_ID="12"\n' > "$tmp/os-debian"
|
||||
check T1-ostag-ubuntu "$(gstack_pw_ostag "$tmp/os-ubuntu")" "ubuntu24.04"
|
||||
check T2-ostag-other "$(gstack_pw_ostag "$tmp/os-debian")" ""
|
||||
|
||||
# ── T3 — errexit safety of the ostag capture (regression: the reproduced
|
||||
# bug aborted the whole caller on every non-Ubuntu host) ──
|
||||
cat > "$tmp/t3.sh" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
source "$L"
|
||||
gstack_pw_ostag "$tmp/os-debian"
|
||||
echo REACHED
|
||||
EOF
|
||||
check T3-errexit-safe "$(bash "$tmp/t3.sh" 2>&1 | tail -1)" "REACHED"
|
||||
|
||||
# ── T4/T5 — pw_supports, no bun involved ─────────────────────────────────
|
||||
mkdir -p "$tmp/pwlib-hit" "$tmp/pwlib-miss"
|
||||
echo "supports ubuntu24.04 and others" > "$tmp/pwlib-hit/index.js"
|
||||
echo "supports nothing relevant" > "$tmp/pwlib-miss/index.js"
|
||||
t4_rc=0; gstack_pw_supports "$tmp/pwlib-hit" ubuntu24.04 >/dev/null 2>&1 \
|
||||
|| t4_rc=$?
|
||||
check T4-supports-hit "$t4_rc" 0
|
||||
t5_rc=0; gstack_pw_supports "$tmp/pwlib-miss" ubuntu24.04 >/dev/null 2>&1 \
|
||||
|| t5_rc=$?
|
||||
check T5-supports-miss "$t5_rc" 1
|
||||
|
||||
# ── T6/T7 — submodule update, real git fixtures ──────────────────────────
|
||||
mkdir -p "$tmp/upstream6"
|
||||
git -C "$tmp/upstream6" init -q -b main; git_id "$tmp/upstream6"
|
||||
printf '{"a":1}\n' > "$tmp/upstream6/package.json"
|
||||
git -C "$tmp/upstream6" add package.json
|
||||
git -C "$tmp/upstream6" commit -q -m init
|
||||
|
||||
mkdir -p "$tmp/repo6"
|
||||
git -C "$tmp/repo6" init -q -b main; git_id "$tmp/repo6"
|
||||
printf 'x\n' > "$tmp/repo6/README.md"
|
||||
git -C "$tmp/repo6" add README.md
|
||||
git -C "$tmp/repo6" commit -q -m init
|
||||
git -C "$tmp/repo6" -c protocol.file.allow=always \
|
||||
submodule add -q -b main "$tmp/upstream6" gstack-sub
|
||||
git -C "$tmp/repo6" config submodule.gstack-sub.branch main
|
||||
git -C "$tmp/repo6" commit -q -m "add submodule"
|
||||
|
||||
printf 'extra\n' > "$tmp/upstream6/extra.txt"
|
||||
git -C "$tmp/upstream6" add extra.txt
|
||||
git -C "$tmp/upstream6" commit -q -m "upstream update"
|
||||
|
||||
t6_out=$(
|
||||
gstack_bump_playwright_if_unsupported() { echo BUMP_CALLED; }
|
||||
gstack_submodule_update_with_bump "$tmp/repo6" "gstack-sub"
|
||||
echo "rc=$?"
|
||||
)
|
||||
t6_calls=$(printf '%s\n' "$t6_out" | grep -c BUMP_CALLED)
|
||||
t6_rc=$(printf '%s\n' "$t6_out" | grep -o 'rc=[0-9]*')
|
||||
check T6-update-success-bumps "$t6_calls:$t6_rc" "1:rc=0"
|
||||
|
||||
mkdir -p "$tmp/upstream7"
|
||||
git -C "$tmp/upstream7" init -q -b main; git_id "$tmp/upstream7"
|
||||
printf '{"a":1}\n' > "$tmp/upstream7/package.json"
|
||||
printf 'lockA\n' > "$tmp/upstream7/bun.lock"
|
||||
git -C "$tmp/upstream7" add package.json bun.lock
|
||||
git -C "$tmp/upstream7" commit -q -m init
|
||||
|
||||
mkdir -p "$tmp/repo7"
|
||||
git -C "$tmp/repo7" init -q -b main; git_id "$tmp/repo7"
|
||||
printf 'x\n' > "$tmp/repo7/README.md"
|
||||
git -C "$tmp/repo7" add README.md
|
||||
git -C "$tmp/repo7" commit -q -m init
|
||||
git -C "$tmp/repo7" -c protocol.file.allow=always \
|
||||
submodule add -q -b main "$tmp/upstream7" gstack-sub
|
||||
git -C "$tmp/repo7" config submodule.gstack-sub.branch main
|
||||
git -C "$tmp/repo7" commit -q -m "add submodule"
|
||||
|
||||
# upstream changes package.json content (would overwrite the local edit)
|
||||
printf '{"a":2}\n' > "$tmp/upstream7/package.json"
|
||||
git -C "$tmp/upstream7" add package.json
|
||||
git -C "$tmp/upstream7" commit -q -m "upstream bumps package.json"
|
||||
# local Playwright-bump-style dirty edit, never committed
|
||||
printf '{"a":99}\n' > "$tmp/repo7/gstack-sub/package.json"
|
||||
|
||||
echo "T7: update-conflict"
|
||||
before_pkg=$(cat "$tmp/repo7/gstack-sub/package.json")
|
||||
before_lock=$(cat "$tmp/repo7/gstack-sub/bun.lock")
|
||||
t7_out=$(gstack_submodule_update_with_bump "$tmp/repo7" "gstack-sub" 2>&1)
|
||||
t7_rc=$?
|
||||
after_pkg=$(cat "$tmp/repo7/gstack-sub/package.json")
|
||||
after_lock=$(cat "$tmp/repo7/gstack-sub/bun.lock")
|
||||
t7_files_ok=N
|
||||
[ "$before_pkg" = "$after_pkg" ] && [ "$before_lock" = "$after_lock" ] \
|
||||
&& t7_files_ok=Y
|
||||
t7_hint_ok=N
|
||||
printf '%s\n' "$t7_out" | grep -q 'make plugin' && t7_hint_ok=Y
|
||||
t7_state="$t7_rc:$t7_files_ok:$t7_hint_ok"
|
||||
check T7-update-conflict-nondestructive "$t7_state" "1:Y:Y"
|
||||
|
||||
# ── T8 — no destructive command anywhere in the lib source ───────────────
|
||||
d8=OK
|
||||
sed 's/#.*//' "$L" | grep -qE 'git [^|;]*(checkout|reset|clean|stash)' && d8=BAD
|
||||
sed 's/#.*//' "$L" | grep -qwE '(rm|rmdir|unlink|truncate|mv)' && d8=BAD
|
||||
check T8-no-destructive-command "$d8" OK
|
||||
|
||||
# ── T9-T14 — browsers-report, fixture cache + playwright-core installs ───
|
||||
mkdir -p "$tmp/installs/fixA/node_modules/playwright-core"
|
||||
cat > "$tmp/installs/fixA/node_modules/playwright-core/browsers.json" <<'EOF'
|
||||
{
|
||||
"comment": "Do not edit this file, use utils/roll_browser.js",
|
||||
"browsers": [
|
||||
{
|
||||
"name": "chromium",
|
||||
"revision": "1228",
|
||||
"installByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "chromium-headless-shell",
|
||||
"revision": "1228",
|
||||
"installByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
"revision": "2311",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"mac14": "2251",
|
||||
"debian11-x64": "2105"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ffmpeg",
|
||||
"revision": "1011",
|
||||
"installByDefault": true
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
cat > "$tmp/installs/fixA/node_modules/playwright-core/package.json" <<'EOF'
|
||||
{
|
||||
"name": "playwright-core",
|
||||
"version": "1.61.1"
|
||||
}
|
||||
EOF
|
||||
|
||||
mkdir -p "$tmp/cache1/.links" \
|
||||
"$tmp/cache1/chromium-1228" \
|
||||
"$tmp/cache1/chromium_headless_shell-1228" \
|
||||
"$tmp/cache1/webkit-2105" \
|
||||
"$tmp/cache1/firefox-9999" \
|
||||
"$tmp/cache1/chromium-9999"
|
||||
printf '%s' "$tmp/installs/fixA/node_modules/playwright-core" \
|
||||
> "$tmp/cache1/.links/link-valid"
|
||||
printf '%s' "$tmp/no-such-install/node_modules/playwright-core" \
|
||||
> "$tmp/cache1/.links/link-broken"
|
||||
|
||||
out1="$(gstack_browsers_report "$tmp/cache1" 2>&1)"
|
||||
has1() { printf '%s\n' "$out1" | grep -q "$1" && echo Y; }
|
||||
check T9-report-referenced "$(has1 'chromium-1228: fixA 1.61.1')" Y
|
||||
check T10-report-underscore-dir \
|
||||
"$(has1 'chromium_headless_shell-1228: fixA 1.61.1')" Y
|
||||
t11_unref=$(has1 'firefox-9999: unreferenced')
|
||||
t11_unknown=$(has1 'chromium-9999: unknown revision')
|
||||
check T11-report-unreferenced "$t11_unref$t11_unknown" YY
|
||||
check T12-report-broken-link "$(has1 '1 broken link')" Y
|
||||
check T13-report-revision-override "$(has1 'webkit-2105: fixA 1.61.1')" Y
|
||||
|
||||
mkdir -p "$tmp/cache2/.links" "$tmp/cache2/chromium-1228"
|
||||
printf '%s' "$tmp/installs/fixA/node_modules/playwright-core" \
|
||||
> "$tmp/cache2/.links/link-valid"
|
||||
out2="$(gstack_browsers_report "$tmp/cache2" 2>&1)"; rc2=$?
|
||||
zero2=$(printf '%s\n' "$out2" | grep -q '0 unreferenced, 0 broken link(s)' \
|
||||
&& echo Y)
|
||||
check T14-report-zero-counts-exit-0 "$rc2:$zero2" "0:Y"
|
||||
|
||||
# ── T15/T16 — degrade silently, nothing on stderr ────────────────────────
|
||||
err15="$(gstack_browsers_report "$tmp/does-not-exist-cache" 2>&1 1>/dev/null)"
|
||||
rc15=$?
|
||||
check T15-report-no-cache "$rc15:[$err15]" "0:[]"
|
||||
|
||||
err16="$(gstack_browsers_report "0" 2>&1 1>/dev/null)"
|
||||
rc16=$?
|
||||
check T16-report-browsers-path-zero "$rc16:[$err16]" "0:[]"
|
||||
|
||||
# ── T17 — sourcing emits nothing ──────────────────────────────────────────
|
||||
out17="$(bash -c "source '$L'; :" 2>&1)"
|
||||
check T17-source-safe "[$out17]" "[]"
|
||||
|
||||
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
|
||||
Reference in New Issue
Block a user