Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

test/cases/84_distribution_model.sh

An object of kind test

What it is

URI
majordomus://test/test/cases/84_distribution_model.sh
Kind
test
Identity
test/cases/84_distribution_model.sh
Path
test/cases/84_distribution_model.sh
Provenance
declared
Media type
text/plain
As a capability
test.test/cases/84_distribution_model.sh

Front matter

{}

Content

# The distribution model is the one place a platform, an artifact name or an installation
# URL is written, and every projection of it agrees. This case proves the invariants the
# rule project.distribution-canonical states, and it proves them by mutation: a target
# added to a copy of the model must change the installer, the matrix and the guide.
# claims: distribution-canonical-model, install-command-is-derived
. "$ROOT/test/lib.sh"
MJB="$(rust_bin)" || rust_bin_exit $?
export MAJORDOMUS_SHARE="$ROOT/share"

# --- the model itself holds ------------------------------------------------------------
expect_exit 0 "$MJB" distribution --repo "$ROOT" validate
"$MJB" distribution --repo "$ROOT" validate 2>/dev/null | grep -q '^OK   distribution' \
  || { echo "    validate said nothing about the model"; exit 1; }

# --- every projection is current -------------------------------------------------------
expect_exit 0 "$MJB" generate --repo "$ROOT" distribution --check

# --- the installer is generated, and is the model's own table --------------------------
inst="$ROOT/site/static/install.sh"
expect_file "$inst"
expect_grep "GENERATED FILE" "$inst"
expect_grep "share/distribution.yaml" "$inst"
"$MJB" distribution --repo "$ROOT" targets 2>/dev/null | awk 'NR>1{print $2}' | while read -r triple; do
  grep -q "$triple" "$inst" || { echo "    the installer does not carry $triple"; exit 1; }
done

# --- the naming function is one function -----------------------------------------------
# every published target derives a distinct name, and the name is the one shape
seen=""
"$MJB" distribution --repo "$ROOT" targets 2>/dev/null | awk 'NR>1 && $3=="supported"{print $1}' | while read -r id; do
  name="$("$MJB" distribution --repo "$ROOT" artifact --target "$id" --tag v9.9.9 --format text 2>/dev/null | sed -n 1p)"
  case "$name" in
    majordomus-v9.9.9-*.tar.gz) ;;
    *) echo "    $id derives '$name', which is not the naming function's shape"; exit 1 ;;
  esac
  case " $seen " in *" $name "*) echo "    two targets derive $name"; exit 1 ;; esac
  seen="$seen $name"
done

# --- the matrix is derived, and carries no tag -----------------------------------------
matrix="$ROOT/docs/generated/distribution-matrix.json"
expect_file "$matrix"
expect_grep '"artifact"' "$matrix"
expect_no_grep 'v[0-9]\.[0-9]\.[0-9]' "$matrix"
count_model="$("$MJB" distribution --repo "$ROOT" targets 2>/dev/null | awk 'NR>1 && ($3=="supported"||$3=="experimental")' | wc -l | tr -d ' ')"
count_matrix="$(grep -c '"target":' "$matrix")"
[ "$count_model" = "$count_matrix" ] \
  || { echo "    the model publishes $count_model target(s) and the matrix builds $count_matrix"; exit 1; }

# --- the one install command reaches every document that states it ---------------------
cmd="$("$MJB" distribution --repo "$ROOT" show --format json 2>/dev/null \
       | sed -n 's/.*"install_command": "\(.*\)",*$/\1/p' | head -n 1)"
[ -n "$cmd" ] || { echo "    the model states no install command"; exit 1; }
# the command is the composition of the model's installer parts, not a string of its own
part() { sed -n "s/^  $1: //p" "$ROOT/share/distribution.yaml" | head -n 1; }
[ "$cmd" = "$(part download_command) $(part base_url)/$(part script) | $(part shell)" ] \
  || { echo "    the install command '$cmd' is not composed from the model's installer parts"; exit 1; }
for doc in "$ROOT/README.md" "$ROOT/docs/INSTALL.md"; do
  grep -qF "$cmd" "$doc" || { echo "    $doc does not carry the canonical install command: $cmd"; exit 1; }
done

# --- the guide's platform table is the model's ------------------------------------------
"$MJB" distribution --repo "$ROOT" targets 2>/dev/null | awk 'NR>1{print $2}' | while read -r triple; do
  grep -q "$triple" "$ROOT/docs/INSTALL.md" \
    || { echo "    the installation guide does not list $triple"; exit 1; }
done

# --- the version is stated in two places and they agree --------------------------------
expect_exit 0 "$ROOT/scripts/release-version" --check
version="$("$ROOT/scripts/release-version")"
expect_exit 10 "$ROOT/scripts/release-version" --check --tag "v0.0.0-not-the-version"
expect_exit 0 "$ROOT/scripts/release-version" --check --tag "v$version"

# --- mutation: a target added to the model reaches every projection ---------------------
cp -R "$ROOT/share" "$T/share"
cat >> "$T/share/distribution.yaml" <<'YAML'

  - id: linux-riscv64-gnu
    os: linux
    arch: x86_64
    libc: gnu
    rust_target: riscv64gc-unknown-linux-gnu
    status: supported
    build:
      runner: ubuntu-24.04
      native: false
YAML
mkdir -p "$T/gen"
MAJORDOMUS_SHARE="$T/share" "$MJB" generate --repo "$ROOT" distribution --out "$T/gen" >/dev/null 2>&1 \
  || { echo "    a target added to the model does not generate"; exit 1; }
expect_grep "riscv64gc-unknown-linux-gnu" "$T/gen/site/static/install.sh"
expect_grep "riscv64gc-unknown-linux-gnu" "$T/gen/docs/generated/distribution-matrix.json"
expect_grep "riscv64gc-unknown-linux-gnu" "$T/gen/docs/INSTALL.md"
# ... and the committed projections are now stale, which is what the drift gate refuses
MAJORDOMUS_SHARE="$T/share" "$MJB" generate --repo "$ROOT" distribution --check >/dev/null 2>&1 \
  && { echo "    a changed model left the committed projections passing --check"; exit 1; }

# --- mutation: the invariants refuse what they say they refuse -------------------------
refuses() { # <edit> <expected fragment>
  rm -rf "$T/bad"; cp -R "$ROOT/share" "$T/bad"
  python3 - "$T/bad/distribution.yaml" "$1" <<'PY' 2>/dev/null || return 0
import sys
path, edit = sys.argv[1], sys.argv[2]
s = open(path).read()
old, new = edit.split('||')
open(path, 'w').write(s.replace(old, new, 1))
PY
  out="$(MAJORDOMUS_SHARE="$T/bad" "$MJB" distribution --repo "$ROOT" validate 2>&1)" && {
    echo "    the model accepted: $1"; return 1; }
  printf '%s\n' "$out" | grep -q "$2" || { echo "    refusal did not name '$2': $out"; return 1; }
  return 0
}
if command -v python3 >/dev/null 2>&1; then
  refuses 'base_url: https://||base_url: http://' 'not https' || exit 1
  refuses 'rust_target: x86_64-apple-darwin||rust_target: aarch64-apple-darwin' 'claim the Rust target' || exit 1
  refuses 'id: macos-x86_64||id: macos-aarch64' 'claim the id' || exit 1
else
  echo "    note: python3 absent; the mutation refusals were not exercised"
fi
exit 0