Registry-only stores, a quieter window, and a CI that builds
ci/woodpecker/manual/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline was successful
A store no longer needs a repository of its own. The engine's built-in defaults already cover the host-to-asset mapping, the install modes, the platforms and the behaviour; what they cannot know is identity — a slug, a name and a catalog URL — and that is exactly what a registry record carries. So `storeRepositoryUrl` is optional: a record with a name and a catalog is a complete store, the id falls back from the repository name to the catalog host (`teletypegames.org` becomes `teletypegames`) to the display name, and the client writes a three-section config. Given a repository it still reads it, and that file stays the authority on how the store behaves; a repository without a config.json is treated as no repository at all. Measured end to end against a local registry serving one record with a null repository: the engine and the core downloaded, engine 1.1.0 accepted the written config, it listed the same ten titles the configured store does, and a hosted title synced into a sandbox with its menu entry written. The "Install all" button is gone, and with it the string it used. Titles are installed one at a time from their own cards. No footer. The window carried a bar at the bottom at all times — a toggle and a line of absolute paths — for something most sessions never need. The log is still there, folder buttons included, behind a quiet switch at the bottom of the side menu; it takes no room until it is opened, and an arriving line does not open it, because the store logs on every refresh and a window that unfolds panels by itself is worse than one that keeps quiet. Three faults that every automated count had passed, found by photographing the setup screen: the store badge rendered as an empty pill with no store open; the gate's picker showed as an empty dropdown stub, because an explicit `display` beats the browser's own `[hidden]` rule; and the gate went up while the empty-catalog line stayed on screen underneath it. The last was a design fault — whether the gate is up was a call on a view rather than state, so the two could disagree. The setup screen is now a field in the state store, and that one field decides which of the gate and the grid is drawn. The window test's gate assertion was wrong too: it demanded a store picker, which only appears when the registry offers more than one store, so one store — the ordinary case — failed it. CI builds the packages this machine cannot. `.woodpecker.yaml` runs the checks on every push and, on a tag or by hand, builds the Linux packages in `electronuserland/builder:22` and the Windows ones in `:22-wine`, then attaches them to the release with scripts/ci-upload.sh. The pipeline lives here rather than in the update server's `/build/config` extension, which serves game-platform pipelines publishing into the site's catalog — a different product with a different target. macOS stays a local build: Apple's toolchain and its signing exist only on a Mac. Both build steps verify what they produced, because a half-finished Wine build leaves a 162 KB stub named like the real installer and `ls` is happy with it. The Linux step was rehearsed locally in the same image (AppImage 128 MB, deb 100 MB); the Wine step cannot be rehearsed on Apple Silicon, where 16 KB host pages break Wine's 4 KB assumption, so the runner is where it is proven. The size check was tested against both outcomes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
# Attach built packages to the Gitea release for this tag.
|
||||
#
|
||||
# The local publisher (scripts/release.sh) drives `tea`, which is logged in
|
||||
# interactively on a workstation. CI has no such session: it has a token and curl. The
|
||||
# two are deliberately separate scripts rather than one with two ways to authenticate —
|
||||
# each is short enough to read in full.
|
||||
#
|
||||
# GITEA_TOKEN=… scripts/ci-upload.sh every package in dist/
|
||||
# GITEA_TOKEN=… scripts/ci-upload.sh dist/one.deb just these
|
||||
#
|
||||
# Assumes the release for the tag exists. It does: the tag is cut on the machine that
|
||||
# builds and publishes the macOS package, and that is what creates the release.
|
||||
set -eu
|
||||
|
||||
FORGE="${FORGE_API:-https://git.teletypegames.org/api/v1}"
|
||||
REPO="${REPO:-${CI_REPO:-}}"
|
||||
TAG="${TAG:-${CI_COMMIT_TAG:-}}"
|
||||
DIST="${DIST:-dist}"
|
||||
|
||||
say() { echo "[ci-upload] $*"; }
|
||||
die() { echo "[ci-upload] error: $*" >&2; exit 1; }
|
||||
|
||||
[ -n "${GITEA_TOKEN:-}" ] || die "GITEA_TOKEN is not set — add the gitea_token secret to the repository"
|
||||
[ -n "$REPO" ] || die "cannot work out the repository — set REPO=owner/name"
|
||||
[ -n "$TAG" ] || die "cannot work out the tag — set TAG=v1.2.3"
|
||||
|
||||
api() {
|
||||
method="$1"; path="$2"; shift 2
|
||||
curl -fsS -X "$method" -H "Authorization: token $GITEA_TOKEN" "$FORGE$path" "$@"
|
||||
}
|
||||
|
||||
# Package names contain spaces — "WarpEngine Store Setup 1.4.0.exe" does — so the list
|
||||
# lives one path per line in a file and is read with `while IFS= read -r`. A single
|
||||
# variable looped over with $list splits on the space and uploads nothing.
|
||||
LIST="$(mktemp)"
|
||||
trap 'rm -f "$LIST"' EXIT
|
||||
if [ "$#" -gt 0 ]; then
|
||||
for given in "$@"; do printf '%s\n' "$given"; done > "$LIST"
|
||||
else
|
||||
# What this pipeline builds. The macOS packages are attached from the Mac that can
|
||||
# sign them, so they are not listed here even when they happen to be present.
|
||||
find "$DIST" -maxdepth 1 -type f \
|
||||
\( -name '*.AppImage' -o -name '*.deb' -o -name '*.exe' \) 2>/dev/null | sort > "$LIST" || true
|
||||
fi
|
||||
[ -s "$LIST" ] || die "no Linux or Windows packages in $DIST"
|
||||
|
||||
say "$REPO $TAG"
|
||||
release_id="$(api GET "/repos/$REPO/releases/tags/$TAG" | jq -r '.id // empty')"
|
||||
[ -n "$release_id" ] || die "no release for $TAG — cut the release first, then re-run this build"
|
||||
|
||||
while IFS= read -r asset; do
|
||||
[ -n "$asset" ] || continue
|
||||
[ -f "$asset" ] || die "no such file: $asset"
|
||||
name="$(basename "$asset")"
|
||||
encoded="$(printf '%s' "$name" | jq -sRr @uri)"
|
||||
|
||||
# Replace rather than refuse, so re-running a build lands.
|
||||
existing="$(api GET "/repos/$REPO/releases/$release_id/assets" |
|
||||
jq -r --arg name "$name" '.[] | select(.name == $name) | .id')"
|
||||
for id in $existing; do
|
||||
say "replacing $name"
|
||||
api DELETE "/repos/$REPO/releases/$release_id/assets/$id" >/dev/null
|
||||
done
|
||||
|
||||
say "uploading $name"
|
||||
api POST "/repos/$REPO/releases/$release_id/assets?name=$encoded" \
|
||||
-F "attachment=@$asset" >/dev/null
|
||||
done < "$LIST"
|
||||
|
||||
say "done:"
|
||||
api GET "/repos/$REPO/releases/$release_id" |
|
||||
jq -r '.assets[] | " \(.name) \(.size / 1000000 | floor) MB"'
|
||||
Reference in New Issue
Block a user