CI / verify (pull_request) Successful in 1m17s
CI / infrastructure-smoke (pull_request) Successful in 31s
CI / package-release (pull_request) Successful in 49s
CI / image-build (pull_request) Successful in 59s
CI / migration-smoke (pull_request) Successful in 2m16s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / package-publication (pull_request) Successful in 56s
CI / deploy-smoke (split) (pull_request) Successful in 2m39s
The package registry moved with the repositories to git.zayin.net/api/packages/Zayin/npm/; the old River_of_Glory_Church address now answers 502, so the next manual Publish packages run would have failed. Point the workflow, the release and module-development docs, and the disposable-Gitea publication proof's owner at Zayin. The CI Docker label keys keep their names: they are identifiers the sweep matches on, not hosts. Co-Authored-By: Claude Opus 5.5 <[email protected]>
137 lines
5.9 KiB
Bash
Executable File
137 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# The package train publication proof (ADR 0053), against a disposable Gitea of the same version as
|
|
# the real registry, never the real one: the publication tool publishes one exact release train,
|
|
# and the suite proves the round trip, partial-train recovery, the hard stop on a burned version,
|
|
# dist-tag convergence, and that the registry's public owner serves packages anonymously while only
|
|
# the publisher team may publish.
|
|
#
|
|
# bash scripts/test-package-publication.sh [--release <release directory>]
|
|
#
|
|
# With --release, it publishes that release (CI hands it the package-release job's artifact).
|
|
# Without, it builds one from a worktree of HEAD, so local changes to the tool are tested against a
|
|
# release of the committed packages.
|
|
set -Eeuo pipefail
|
|
|
|
repository_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$repository_root"
|
|
|
|
# shellcheck source=scripts/lib/compose-job-network.sh
|
|
source "$repository_root/scripts/lib/compose-job-network.sh"
|
|
# shellcheck source=scripts/lib/compose-diagnostics.sh
|
|
source "$repository_root/scripts/lib/compose-diagnostics.sh"
|
|
# shellcheck source=scripts/lib/ci-project.sh
|
|
source "$repository_root/scripts/lib/ci-project.sh"
|
|
|
|
fail() {
|
|
echo "package publication proof failed: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
release=""
|
|
while (($#)); do
|
|
case "$1" in
|
|
--release)
|
|
release="${2:-}"
|
|
shift 2 || fail "--release needs a directory"
|
|
;;
|
|
*) fail "usage: test-package-publication.sh [--release <release directory>]" ;;
|
|
esac
|
|
done
|
|
|
|
command -v docker >/dev/null || fail "Docker is required"
|
|
docker compose version >/dev/null 2>&1 || fail "Docker Compose is required"
|
|
compose_job_network_initialize || fail "the current Docker job container could not be identified"
|
|
|
|
project="$(ci_project_name package-registry)"
|
|
work="$(mktemp -d "${TMPDIR:-/tmp}/ekklesia-package-publication-XXXXXX")"
|
|
worktree="$work/release-checkout"
|
|
|
|
if [[ "$COMPOSE_JOB_NETWORK_RUNNING_IN_CONTAINER" == "true" ]]; then
|
|
export GITEA_PORT="0" GITEA_ROOT_URL="http://gitea:3000/"
|
|
else
|
|
GITEA_PORT="$(node -e 'const s = require("net").createServer().listen(0, "127.0.0.1", () => { console.log(s.address().port); s.close(); })')"
|
|
export GITEA_PORT GITEA_ROOT_URL="http://127.0.0.1:$GITEA_PORT/"
|
|
fi
|
|
compose=(docker compose --project-name "$project" --file scripts/lib/package-registry.compose.yaml)
|
|
|
|
cleanup() {
|
|
local exit_status=$?
|
|
trap - EXIT INT TERM
|
|
compose_job_network_disconnect || exit_status=1
|
|
"${compose[@]}" down --volumes --remove-orphans --timeout 10 >/dev/null 2>&1 || exit_status=1
|
|
if [[ -d "$worktree" ]]; then
|
|
git -C "$repository_root" worktree remove --force "$worktree" >/dev/null 2>&1 || true
|
|
fi
|
|
rm -rf "$work"
|
|
exit "$exit_status"
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
|
|
if [[ -z "$release" ]]; then
|
|
echo "==> Building a release of HEAD to publish"
|
|
store="$(dirname "$(pnpm store path)")"
|
|
git worktree add --detach --quiet "$worktree" HEAD
|
|
(cd "$worktree" && pnpm install --offline --frozen-lockfile --store-dir "$store" --reporter=silent)
|
|
node "$worktree/scripts/package-release.ts" --out "$work/release" >/dev/null
|
|
release="$(find "$work/release" -mindepth 1 -maxdepth 1 -type d)"
|
|
fi
|
|
release="$(cd "$release" && pwd)"
|
|
|
|
# Masked in any Compose diagnostics below.
|
|
COMPOSE_DIAGNOSTICS_SECRETS=()
|
|
|
|
echo "==> Starting a disposable Gitea 1.27.2"
|
|
"${compose[@]}" config --quiet || fail "the disposable registry's configuration is invalid"
|
|
if ! up_output="$("${compose[@]}" up --detach --wait --wait-timeout 180 gitea 2>&1)"; then
|
|
compose_report_not_ready "$up_output" "${compose[@]}"
|
|
fail "the disposable Gitea did not become ready"
|
|
fi
|
|
if [[ "$COMPOSE_JOB_NETWORK_RUNNING_IN_CONTAINER" == "true" ]]; then
|
|
compose_job_network_connect gitea "${compose[@]}" || fail "the job could not join the registry's network"
|
|
fi
|
|
origin="${GITEA_ROOT_URL%/}"
|
|
|
|
admin_password="admin-$RANDOM$RANDOM$RANDOM-$RANDOM"
|
|
publisher_password="publisher-$RANDOM$RANDOM$RANDOM-$RANDOM"
|
|
outsider_password="outsider-$RANDOM$RANDOM$RANDOM-$RANDOM"
|
|
"${compose[@]}" exec -T -u git gitea gitea admin user create --admin --username ekklesia-admin \
|
|
--password "$admin_password" --email [email protected] --must-change-password=false >/dev/null
|
|
|
|
api() {
|
|
local method="$1" path="$2" body="${3:-}" auth="${4:-ekklesia-admin:$admin_password}"
|
|
curl -fsS -X "$method" -u "$auth" -H "Content-Type: application/json" \
|
|
${body:+--data "$body"} "$origin/api/v1$path"
|
|
}
|
|
|
|
echo "==> Creating public package owners, the publisher team, and an outsider"
|
|
for user in publisher outsider; do
|
|
password_variable="${user}_password"
|
|
api POST /admin/users "{\"username\":\"$user\",\"password\":\"${!password_variable}\",\"email\":\"$user@example.invalid\",\"must_change_password\":false}" >/dev/null
|
|
done
|
|
owners=(Zayin ekklesia-burned-version ekklesia-tag-retry ekklesia-artifact-layout)
|
|
for owner in "${owners[@]}"; do
|
|
api POST /orgs "{\"username\":\"$owner\",\"visibility\":\"public\"}" >/dev/null
|
|
team="$(api POST "/orgs/$owner/teams" '{"name":"package-publishers","includes_all_repositories":false,"can_create_org_repo":false,"units":["repo.packages"],"units_map":{"repo.packages":"write"}}' |
|
|
node -e 'process.stdout.write(String(JSON.parse(require("fs").readFileSync(0, "utf8")).id))')"
|
|
api PUT "/teams/$team/members/publisher" >/dev/null
|
|
done
|
|
token_for() {
|
|
local user="$1" password="$2"
|
|
api POST "/users/$user/tokens" "{\"name\":\"publish-$RANDOM\",\"scopes\":[\"write:package\"]}" "$user:$password" |
|
|
node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(0, "utf8")).sha1)'
|
|
}
|
|
publish_token="$(token_for publisher "$publisher_password")"
|
|
outsider_token="$(token_for outsider "$outsider_password")"
|
|
|
|
echo "==> Publishing the release train to it"
|
|
EKKLESIA_TEST_REGISTRY_ORIGIN="$origin" \
|
|
EKKLESIA_TEST_RELEASE="$release" \
|
|
EKKLESIA_TEST_PUBLISH_TOKEN="$publish_token" \
|
|
EKKLESIA_TEST_OUTSIDER_TOKEN="$outsider_token" \
|
|
EKKLESIA_TEST_OWNERS="${owners[*]}" \
|
|
pnpm exec vitest run --config vitest.packages.integration.config.ts
|
|
|
|
echo "Package publication proof passed."
|