Signage had no CI. Its tests passed while three defects shipped that appear only inside Ekklesia, in a real browser. Every job now assembles Signage into a pinned platform commit, as an instance does, and installs that workspace with --frozen-lockfile: - lint, typecheck, test: Signage's own checks; - build: the platform's gates against exactly this module set (boundary rules, module manifest, migrations), then the build; - integration: the deployment image from the platform's Dockerfile, then the platform's split-topology smoke. It pairs a kiosk display, restarts it on the same profile, and runs the scenarios in ci/display-scenarios. The supporting files: - ci/platform.env pins one platform commit. ci/assemble.sh fetches the platform's main and refuses a pinned commit that is not on it, so CI only runs reviewed platform code. - ci/pnpm-lock.yaml is the assembled workspace's lockfile, regenerated with ci/update-lock.sh. Same trust rules as the platform: actions pinned by SHA, persist-credentials false, no secrets, no pull_request_target. Co-Authored-By: Claude Opus 5.5 <[email protected]>
73 lines
3.2 KiB
Bash
Executable File
73 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Assembles Signage into the Ekklesia platform it is tested against: the platform at the commit pinned
|
|
# in ci/platform.env, this checkout at modules/signage, and ci/pnpm-lock.yaml as the workspace
|
|
# lockfile. Signage cannot build or run outside an Ekklesia workspace, so every CI job starts here.
|
|
#
|
|
# bash ci/assemble.sh [--allow-missing-lock] [output directory]
|
|
#
|
|
# The output directory (default build/workspace) is deleted and recreated. The platform is fetched
|
|
# by its `main` branch and the pinned commit must be on it, so CI only ever runs reviewed platform
|
|
# code; the checkout is then by commit, so nothing after it depends on a ref the network can move.
|
|
# Every .git directory is removed afterwards.
|
|
#
|
|
# --allow-missing-lock keeps the platform's own lockfile. Only ci/update-lock.sh uses it.
|
|
set -Eeuo pipefail
|
|
|
|
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
allow_missing_lock=0
|
|
if [[ "${1:-}" == "--allow-missing-lock" ]]; then
|
|
allow_missing_lock=1
|
|
shift
|
|
fi
|
|
out="${1:-$root/build/workspace}"
|
|
|
|
fail() {
|
|
echo "assemble: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
for command in git tar; do
|
|
command -v "$command" >/dev/null || fail "$command is required."
|
|
done
|
|
|
|
# Read the two pins without sourcing the file: it is data, not a script.
|
|
pin() {
|
|
local value
|
|
value="$(sed -n "s/^$1=//p" "$root/ci/platform.env")"
|
|
[[ -n "$value" ]] || fail "ci/platform.env has no $1."
|
|
printf '%s' "$value"
|
|
}
|
|
repository="$(pin EKKLESIA_PLATFORM_REPOSITORY)"
|
|
commit="$(pin EKKLESIA_PLATFORM_COMMIT)"
|
|
[[ "$commit" =~ ^[0-9a-f]{40}$ ]] || fail "EKKLESIA_PLATFORM_COMMIT must be a full commit SHA."
|
|
|
|
rm -rf "$out"
|
|
mkdir -p "$(dirname -- "$out")"
|
|
git init --quiet "$out"
|
|
git -C "$out" remote add origin "$repository"
|
|
git -C "$out" fetch --quiet --no-tags origin "+refs/heads/main:refs/remotes/origin/main" ||
|
|
fail "cannot fetch the platform's main from ${repository}."
|
|
git -C "$out" cat-file -e "${commit}^{commit}" 2>/dev/null ||
|
|
fail "the pinned platform commit ${commit} is not on the platform's main."
|
|
git -C "$out" merge-base --is-ancestor "$commit" refs/remotes/origin/main ||
|
|
fail "the pinned platform commit ${commit} is not on the platform's main."
|
|
git -C "$out" -c advice.detachedHead=false checkout --quiet --detach "$commit"
|
|
[[ "$(git -C "$out" rev-parse HEAD)" == "$commit" ]] || fail "the platform did not check out at ${commit}."
|
|
rm -rf "$out/.git"
|
|
echo "assemble: platform ${commit}"
|
|
|
|
[[ ! -e "$out/modules/signage" ]] || fail "the platform checkout already has modules/signage."
|
|
mkdir -p "$out/modules/signage"
|
|
# Exactly the files this checkout tracks, as committed or as changed in the working tree. Nothing
|
|
# installed, built, or ignored, and not the CI scaffolding, which is not part of the module.
|
|
git -C "$root" ls-files -z --cached --others --exclude-standard -- . ':!ci' ':!.gitea' |
|
|
tar -C "$root" --null --files-from=- --create --file=- |
|
|
tar -C "$out/modules/signage" --extract --file=-
|
|
echo "assemble: signage $(git -C "$root" rev-parse HEAD 2>/dev/null || echo "(working tree)")"
|
|
|
|
if [[ -f "$root/ci/pnpm-lock.yaml" ]]; then
|
|
cp "$root/ci/pnpm-lock.yaml" "$out/pnpm-lock.yaml"
|
|
elif ((allow_missing_lock == 0)); then
|
|
fail "ci/pnpm-lock.yaml is missing; run bash ci/update-lock.sh."
|
|
fi
|