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:
@@ -65,10 +65,13 @@ export class SelfTestRunner {
|
||||
|
||||
if (this.shotPath !== null) await this.captureShot(this.shotPath)
|
||||
|
||||
// A gate passes on having something to do, not on having a picker: the picker
|
||||
// only appears when the registry offers more than one store, and one store is
|
||||
// the ordinary case. Requiring choices here failed a perfectly good window.
|
||||
const rendered = report.locales.length > 1 && (
|
||||
(report.cards > 0 && !report.gateVisible && report.stores.length > 0 &&
|
||||
report.categories.length > 0 && report.activeCategory !== null) ||
|
||||
(report.gateVisible && report.gateChoices.length > 0 && report.gateAction.length > 0))
|
||||
(report.gateVisible && report.gateAction.length > 0))
|
||||
const switchedWell = switched === null || (
|
||||
switched.storeId.length > 0 && switched.storeId !== report.storeId &&
|
||||
switched.cards > 0 && switched.categories > 0)
|
||||
|
||||
@@ -27,14 +27,16 @@ export function requireStringArray (value: unknown, name: string): readonly stri
|
||||
export function requireRegistryStore (value: unknown): RegistryStoreDto {
|
||||
const record = asRecord(value)
|
||||
if (record === null) throw new TypeError('a store record is required')
|
||||
const repository = readString(record, 'storeRepositoryUrl')
|
||||
const store: RegistryStoreDto = {
|
||||
name: readString(record, 'name'),
|
||||
catalogUrl: readString(record, 'catalogUrl'),
|
||||
storeRepositoryUrl: readString(record, 'storeRepositoryUrl'),
|
||||
// Optional: a store with no repository installs on the engine's defaults.
|
||||
storeRepositoryUrl: repository.length > 0 ? repository : null,
|
||||
storeId: readString(record, 'storeId')
|
||||
}
|
||||
if (store.name.length === 0 || store.catalogUrl.length === 0 || store.storeRepositoryUrl.length === 0) {
|
||||
throw new TypeError('a store record needs a name, a catalog URL and a repository URL')
|
||||
if (store.name.length === 0 || store.catalogUrl.length === 0) {
|
||||
throw new TypeError('a store record needs a name and a catalog URL')
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user