Two gaps that were the same gap: the store list could only ever grow, and it could only grow from what the registry happened to offer. **Removing** uninstalls what the store installed, then deletes the store itself, in that order. The order is the whole of it: `state.json` is the only record of which payloads, icons and menu entries belong to a store, so deleting the home first would strip the one thing that knows — leaving files nothing could ever identify, least of all a later install of the same store into the same folder. The confirmation says how many titles will go, because that is the part nobody would otherwise expect. The token goes too; a credential for a store that is not here is a secret kept for nothing. The window names a *store*, never a path: the home is resolved against what a disk scan actually found before anything is deleted, and `removeHome` refuses anything else. That is the only guard between a bad argument and `rm -rf`, so it has a test. **Adding** moved to a + beside Refresh — both are actions on the whole store rather than on one of them, and the full-width button under the list read as a third store — and the picker now takes a catalog address as well as a listed one. A bare host is enough and the name comes from the address; nothing else about installing changes, which is why the typed path hands the same record to the same method instead of growing a second one. The picker also has a Cancel now: opening it with a store installed used to replace the grid with no way back. `make storetest` is new, and it earned itself immediately. Removal is the only code here that deletes a directory tree, which the smoke test cannot cover — it runs against the real machine and would have to delete a real store to prove anything. Two bugs on the first run: - `http://` was accepted and became a store called *http*. The trailing slashes were stripped before the scheme was checked, turning `http://` into `http:` and then into `https://http:`, whose hostname parses as "http". The URL is rebuilt from the parsed form now, which also settles the trailing slash in one place. - `STORE_ROOT` only *prepended* to the search path, so a "sandboxed" run still listed the real stores — despite the README saying "instead of the real one". Harmless while a sandbox could only add; not harmless now that it can delete. It replaces the search path. The self-test needed two changes, both of which are it working: the store row is a wrapper now, so clicking `.store-row` did nothing at all, and the footer icon check counted exactly two named controls when there are three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
126 lines
4.9 KiB
Makefile
126 lines
4.9 KiB
Makefile
# WarpEngine Client — the front door to the npm scripts.
|
|
#
|
|
# Everything here is a thin wrapper: the app is an Electron project, so npm still
|
|
# does the work. The Makefile exists so the useful sequences have names, and so
|
|
# `make release` is one command rather than a build followed by remembered tea
|
|
# invocations.
|
|
#
|
|
# make list the targets
|
|
# make setup install the dependencies
|
|
# make check typecheck, lint and both test suites
|
|
# make dist package for this machine
|
|
# make release package and publish to Gitea
|
|
#
|
|
# Publishing assumes `tea` is installed and logged in — the devarea repo has
|
|
# `make tea` for that.
|
|
|
|
SHELL := /bin/sh
|
|
SCRIPTS := scripts
|
|
NODE_MIN := 22
|
|
|
|
# The version is package.json's, so the release tag never drifts from the app. Read with
|
|
# node, which this project already requires — nothing here needs an interpreter the app
|
|
# itself no longer depends on.
|
|
VERSION := $(shell node -p 'require("./package.json").version')
|
|
TAG ?= v$(VERSION)
|
|
|
|
# Which site's store registry a packaged build reads. Empty means the default in
|
|
# package.json (ours); set it to build a client for somebody else's catalog:
|
|
#
|
|
# make dist STORES_API=https://games.example.org/api/stores
|
|
#
|
|
# It is baked into the package's own package.json, so the built app carries it. A runtime
|
|
# STORES_API still overrides it, which is for trying something out rather than shipping.
|
|
STORES_API ?=
|
|
BUILDER_ARGS := $(if $(STORES_API),-- --config.extraMetadata.warpEngine.registryUrl=$(STORES_API),)
|
|
|
|
.DEFAULT_GOAL := help
|
|
|
|
.PHONY: help setup node-check build typecheck lint lint-fix check start smoke uitest storetest test \
|
|
dist dist-mac dist-win dist-linux release publish clean distclean version
|
|
|
|
help: ## List available targets
|
|
@echo "WarpEngine Client $(VERSION) — usage: make <target>"
|
|
@echo
|
|
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | \
|
|
awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}'
|
|
@echo
|
|
@echo " Variables: TAG=$(TAG) TEA_LOGIN=ttg REPO=<owner/name> NOTES=RELEASE_NOTES.md"
|
|
|
|
node-check: ## Check the Node version Electron's installer needs
|
|
@node -e 'const [maj] = process.versions.node.split("."); \
|
|
if (Number(maj) < $(NODE_MIN)) { \
|
|
console.error("Node $(NODE_MIN)+ is needed to install Electron (found " + process.versions.node + \
|
|
"): its installer is ESM-only. The packaged app carries its own runtime."); \
|
|
process.exit(1); \
|
|
} else { console.log("node " + process.versions.node + " ok"); }'
|
|
|
|
setup: node-check ## Install the dependencies
|
|
npm install
|
|
|
|
build: ## Compile TypeScript and bundle the preload and the renderer
|
|
npm run build
|
|
|
|
typecheck: ## Type-check everything, emitting nothing
|
|
npm run typecheck
|
|
|
|
lint: ## Lint with the strict rule set
|
|
npm run lint
|
|
|
|
lint-fix: ## Lint and fix what can be fixed automatically
|
|
npm run lint:fix
|
|
|
|
# The order is deliberate: a type error explains a lint error, and both explain a
|
|
# failing test, so the cheapest check that can fail runs first.
|
|
check: typecheck lint test ## Type-check, lint, and run every test suite
|
|
|
|
start: ## Run the app against whatever store is installed
|
|
npm start
|
|
|
|
smoke: ## Drive the store bridge with no window at all
|
|
npm run smoke
|
|
|
|
uitest: ## Load the window once and report what rendered
|
|
npm run uitest
|
|
|
|
storetest: ## Add and remove a store in a sandbox (the only code that deletes a tree)
|
|
npm run storetest
|
|
|
|
test: smoke storetest uitest ## All three checks
|
|
|
|
dist: node-check ## Package for this machine
|
|
npm run dist $(BUILDER_ARGS)
|
|
|
|
dist-mac: node-check ## Package for macOS (ad-hoc signed, see the README)
|
|
npm run dist:mac $(BUILDER_ARGS)
|
|
|
|
dist-win: node-check ## Package for Windows
|
|
npm run dist:win $(BUILDER_ARGS)
|
|
|
|
dist-linux: node-check ## Package for Linux
|
|
npm run dist:linux $(BUILDER_ARGS)
|
|
|
|
publish: ## Upload the packages already in dist/ to the Gitea release
|
|
@TAG=$(TAG) $(SCRIPTS)/release.sh
|
|
|
|
# clean first: dist/ keeps earlier builds, and a release should be made of exactly
|
|
# what this version produced.
|
|
release: clean dist publish ## Package for this machine and publish it
|
|
|
|
clean: ## Remove the compiled output and the built packages
|
|
rm -rf build dist
|
|
|
|
distclean: clean ## Remove the packages and the dependencies
|
|
rm -rf node_modules
|
|
|
|
version: ## Show the versions involved
|
|
@echo "app $(VERSION) (tag $(TAG))"
|
|
@printf "node "; node --version 2>/dev/null || echo "missing"
|
|
@printf "npm "; npm --version 2>/dev/null || echo "missing"
|
|
@printf "electron "; node -p "require('./package.json').devDependencies.electron" 2>/dev/null || echo "missing"
|
|
@printf "typescript "; npx tsc --version 2>/dev/null || echo "missing"
|
|
@printf "eslint "; npx eslint --version 2>/dev/null || echo "missing"
|
|
@printf "tea "; tea --version 2>/dev/null | head -1 || echo "missing — devarea: make tea"
|
|
@printf "registry "; node -p 'require("./package.json").warpEngine.registryUrl'
|
|
@if [ -n "$(STORES_API)" ]; then printf " build override: %s\n" "$(STORES_API)"; fi
|