#!/usr/bin/env bash
# Compila o Tessel do fredygames.com: Tessel 1.0.23 (GPL-3.0-or-later), de Gijs van Tulder, a partir do
# tarball do commit c70d63c do repositorio gvtulder/tessel, como no "npm run build" do autor (lingui
# compile + parcel build), com tres diferencas na chamada: SKIP_SERVICE_WORKER=true (o jogo nao registra service
# worker nem guarda o site em cache no navegador do visitante), --public-url ./ (o jogo mora numa
# subpasta) e --no-cache. Este script e o patch ao lado dele sao parte do codigo-fonte publicado junto
# do jogo ("the scripts used to control compilation" da GPL).
#
# src/FredGames.Web/Content/games/tessel/engine/build.sh
#
# Node 24.21.0 (o oficial, darwin-arm64, conferido pelo SHA-256; traz o npm 11.19.0) fica em
# artifacts/tools/node-v24.21.0. "npm ci" instala exatamente as versoes do package-lock.json do commit
# (o npm confere a integridade de cada pacote), com o cache dentro da pasta de saida. Dois builds
# seguidos dao os mesmos bytes.
#
# tessel-fredygames.patch:
# - babel.config.js: a versao mostrada nas configuracoes leva o commit fixo (o tarball nao tem .git);
# - src/index.html: sem o (instalar como app) e sem a meta apple-itunes-app
# (faixa da App Store no Safari); links para outros sites abrem em outra aba (o jogo roda num
# iframe, onde paginas como o GitHub nao abrem);
# - src/main.browser.ts: nas paginas /pt-br/ do site o idioma padrao e o portugues do Brasil (um
# idioma escolhido nas configuracoes continua valendo).
# - src/main.shared.ts: o jogo nao pula para a raiz do site ("/") quando o caminho e outro (aqui ele
# mora em bundles/tessel//index.html).
# - src/ui/shared/icons.ts: o icone de livro (botao "How to play?") era do Font Awesome Pro, de licenca
# comercial; no lugar dele vai o "book-open" do Font Awesome Free 6.7.2 (CC BY 4.0), como os demais.
#
# Saida: artifacts/build/tessel-1.0.23/dist/, que o bundle.yaml do jogo confere arquivo por arquivo.
set -euo pipefail
VERSION=1.0.23
COMMIT=c70d63cc76485389a963ab1d63cf431e2a3e9d8d
SHA256=eb58c3ba032a7c31db44e3653e62ab0abd35429be33628b1baae337c3869c465
NODE=24.21.0
NODE_SHA256=bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)"
out="$root/artifacts/build/tessel-$VERSION"
cache="$root/artifacts/build/cache"
tools="$root/artifacts/tools"
mkdir -p "$cache"
fetch() {
if [ ! -f "$cache/$2" ]; then
curl -fsSL --retry 4 "$1" -o "$cache/$2.partial" && mv "$cache/$2.partial" "$cache/$2"
fi
echo "$3 $cache/$2" | shasum -a 256 -c -
}
fetch "https://github.com/gvtulder/tessel/archive/$COMMIT.tar.gz" "tessel-${COMMIT:0:7}.tar.gz" "$SHA256"
fetch "https://nodejs.org/dist/v$NODE/node-v$NODE-darwin-arm64.tar.gz" "node-v$NODE-darwin-arm64.tar.gz" "$NODE_SHA256"
if [ ! -d "$tools/node-v$NODE" ]; then
mkdir -p "$tools/node-v$NODE.tmp"
tar -xzf "$cache/node-v$NODE-darwin-arm64.tar.gz" -C "$tools/node-v$NODE.tmp"
mv "$tools/node-v$NODE.tmp/node-v$NODE-darwin-arm64" "$tools/node-v$NODE"
rm -rf "$tools/node-v$NODE.tmp"
fi
rm -rf "$out"
mkdir -p "$out/tree" "$out/home"
tar -xzf "$cache/tessel-${COMMIT:0:7}.tar.gz" -C "$out/tree" --strip-components 1
patch -d "$out/tree" -p1 --quiet < "$here/tessel-fredygames.patch"
# Ambiente isolado: o Node fixo no PATH, o cache e a configuracao do npm dentro da pasta de saida.
export PATH="$tools/node-v$NODE/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export HOME="$out/home" npm_config_cache="$out/home/npm-cache" npm_config_userconfig="$out/home/npmrc"
export npm_config_update_notifier=false npm_config_fund=false npm_config_audit=false
export TZ=UTC LC_ALL=C SOURCE_DATE_EPOCH=1782919048
cd "$out/tree"
npm ci --no-progress --loglevel=error
npx lingui compile --typescript
NODE_ENV=production SKIP_SERVICE_WORKER=true npx parcel build src/index.html \
--no-source-maps --no-cache --public-url ./ --dist-dir "$out/dist"
# Bibliotecas de node_modules que entram no game.*.js (dependencias de execucao do package.json e as
# dependencias delas; o resto do package-lock.json e ferramenta de build): o fonte delas, como o npm as
# instalou, vai num tarball deterministico, e as licencas num texto unico.
RUNTIME_DEPS="@lingui/core @lingui/message-utils @messageformat/parser @messageformat/date-skeleton js-sha256
canonical-json check2d sat poly-decomp-es polygon-clipping robust-predicates splaytree
workbox-core workbox-window workbox-precaching workbox-routing workbox-strategies @parcel/service-worker zod"
/usr/bin/python3 - "$out" $RUNTIME_DEPS <<'PY'
import gzip, io, os, sys, tarfile
out, deps = sys.argv[1], sys.argv[2:]
mtime = int(os.environ["SOURCE_DATE_EPOCH"])
base = os.path.join(out, "tree", "node_modules")
raw = io.BytesIO()
with tarfile.open(fileobj=raw, mode="w", format=tarfile.PAX_FORMAT) as tar:
for dep in deps:
files = []
for d, dirs, names in os.walk(os.path.join(base, dep)):
dirs.sort()
files += [os.path.join(d, n) for n in names]
for f in sorted(files):
info = tar.gettarinfo(f, "node_modules/" + os.path.relpath(f, base))
info.mtime, info.uid, info.gid, info.uname, info.gname = mtime, 0, 0, "", ""
info.mode = 0o755 if info.mode & 0o111 else 0o644
with open(f, "rb") as fh:
tar.addfile(info, fh)
with open(os.path.join(out, "tessel-runtime-deps.tar.gz"), "wb") as fh:
with gzip.GzipFile(fileobj=fh, mode="wb", mtime=0, filename="") as gz:
gz.write(raw.getvalue())
with open(os.path.join(out, "THIRD-PARTY-LICENSES.txt"), "w") as fh:
for dep in deps:
d = os.path.join(base, dep)
names = sorted(n for n in os.listdir(d) if n.lower().startswith(("licen", "copying", "unlicense")))
import json
meta = json.load(open(os.path.join(d, "package.json")))
fh.write("=" * 78 + "\n%s %s (%s)\n" % (dep, meta["version"], meta.get("license")) + "=" * 78 + "\n\n")
for n in names:
fh.write(open(os.path.join(d, n), encoding="utf-8").read().rstrip() + "\n\n")
if not names:
fh.write("(no licence file in the package; package.json declares %s)\n\n" % meta.get("license"))
PY
echo "Tessel $VERSION em $out/dist:"
(cd "$out/dist" && find . -type f | LC_ALL=C sort | sed 's#^\./##' | while read -r f; do
echo "$(shasum -a 256 "$f" | cut -d' ' -f1) $f"
done)
shasum -a 256 "$out/tessel-runtime-deps.tar.gz" "$out/THIRD-PARTY-LICENSES.txt"