cdsr: initial import of deployed stack (runtime 4.133.0, pinned extensions, SSOT vault)
This commit is contained in:
106
scripts/list-extension-versions.sh
Executable file
106
scripts/list-extension-versions.sh
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
# Audit installed extensions against config/extensions.lock.json.
|
||||
# Fails if a pinned extension is missing/at another version, if an expected transitive
|
||||
# dependency is at another version, or if anything is installed that the lock does not
|
||||
# describe at all (ad-hoc marketplace installs are contract violations here).
|
||||
# ./scripts/list-extension-versions.sh # baseline audit
|
||||
# ./scripts/list-extension-versions.sh --include-optional # also require optional entries
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
LOCK="$ROOT/config/extensions.lock.json"
|
||||
CODE_SERVER="${CODE_SERVER_BIN:-/usr/bin/code-server}"
|
||||
CONFIG="$ROOT/config/config.yaml"
|
||||
|
||||
INCLUDE_OPTIONAL=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--include-optional) INCLUDE_OPTIONAL=1 ;;
|
||||
*) echo "unknown argument: $arg" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
installed=$("$CODE_SERVER" --config "$CONFIG" --list-extensions --show-versions 2>/dev/null | sed '/^$/d' | sort)
|
||||
|
||||
echo "installed:"
|
||||
printf '%s\n' "$installed" | sed 's/^/ /'
|
||||
echo
|
||||
|
||||
INSTALLED="$installed" INCLUDE_OPTIONAL="$INCLUDE_OPTIONAL" python3 - "$LOCK" <<'PY'
|
||||
import json, os, sys
|
||||
|
||||
lock = json.load(open(sys.argv[1]))
|
||||
want_optional = os.environ.get("INCLUDE_OPTIONAL") == "1"
|
||||
installed = {}
|
||||
for line in os.environ.get("INSTALLED", "").splitlines():
|
||||
line = line.strip()
|
||||
if "@" in line:
|
||||
eid, _, ver = line.rpartition("@")
|
||||
installed[eid.lower()] = ver
|
||||
|
||||
status = 0
|
||||
known = set()
|
||||
|
||||
# 1. forward check: every selected pin must be installed at the pinned version
|
||||
selected = [
|
||||
e for e in lock["extensions"]
|
||||
if e["mode"] == "baseline" or (want_optional and e["mode"] == "optional")
|
||||
]
|
||||
for e in selected:
|
||||
key = e["id"].lower()
|
||||
known.add(key)
|
||||
want = f'{e["id"]}@{e["version"]}'
|
||||
have = installed.get(key)
|
||||
if have == e["version"]:
|
||||
print(f"ok {want}")
|
||||
elif have is None:
|
||||
print(f"MISSING {want}", file=sys.stderr)
|
||||
status = 1
|
||||
else:
|
||||
print(f'MISMATCH expected {want}, found {e["id"]}@{have}', file=sys.stderr)
|
||||
status = 1
|
||||
|
||||
# 2. expected transitive dependencies: pulled in by the pins above, so their presence is
|
||||
# legitimate; absence is not a lock violation, but a version drift is reported and is
|
||||
# labelled MISMATCH-DEP so it is never confused with a baseline pin mismatch.
|
||||
for e in lock.get("expected_transitive", []):
|
||||
key = e["id"].lower()
|
||||
known.add(key)
|
||||
want = f'{e["id"]}@{e["version"]}'
|
||||
have = installed.get(key)
|
||||
if have == e["version"]:
|
||||
print(f"ok-dep {want}")
|
||||
elif have is None:
|
||||
print(f"note {want} (expected transitive dependency not installed)")
|
||||
else:
|
||||
print(
|
||||
f'MISMATCH-DEP expected transitive {want}, found {e["id"]}@{have}',
|
||||
file=sys.stderr,
|
||||
)
|
||||
status = 1
|
||||
|
||||
# 3. lock-described but not selected (e.g. optional entries in a baseline run): installed
|
||||
# copies are informational, never failures, because the lock does describe them.
|
||||
for e in lock["extensions"]:
|
||||
key = e["id"].lower()
|
||||
if key in known:
|
||||
continue
|
||||
known.add(key)
|
||||
have = installed.get(key)
|
||||
if have is None:
|
||||
continue
|
||||
suffix = "" if have == e["version"] else f' (lock pins {e["version"]})'
|
||||
print(f'note {e["id"]}@{have} installed, {e["mode"]} entry not selected{suffix}')
|
||||
|
||||
# 4. reverse check: anything installed that the lock does not describe at all
|
||||
excluded = {e["id"].lower(): e.get("reason", "") for e in lock.get("excluded", [])}
|
||||
for eid, ver in sorted(installed.items()):
|
||||
if eid in known:
|
||||
continue
|
||||
why = excluded.get(eid)
|
||||
detail = f" (explicitly excluded by lock: {why})" if why else ""
|
||||
print(f"EXTRA {eid}@{ver}{detail}", file=sys.stderr)
|
||||
status = 1
|
||||
|
||||
sys.exit(status)
|
||||
PY
|
||||
Reference in New Issue
Block a user