#!/usr/bin/env bash # Compila o PicReact (nonograma com puzzles aleatorios e editor proprio, GPL-3.0, bgonp; React 18 + wouter + Vite 4) a partir do # commit 0be1cfd de github.com/bgonp/picreact, com o "npm ci" do package-lock.json do autor (hashes de integridade do npm) e # "vite build --base=./", com o Node 24.21.0 de artifacts/tools. # # Mudancas, todas feitas aqui (cada uma falha se o texto esperado nao existir): # - rotas por hash (wouter com um hook de #/rota, a receita da propria documentacao do wouter 2): o jogo roda numa pasta/iframe e # nao na raiz de um dominio; # - sai o botao de compartilhar (montava um link com o endereco da pagina e copiava ou chamava navigator.share; num iframe o # endereco nao serve) e o texto que o menciona; # - o rodape perde os links para o GitHub do autor e o "How to play": fica o credito e o nome da licenca. # O jogo nao tem imagem, som nem fonte web (so fontes do sistema) e nao faz requisicoes. # # src/FredGames.Bundler/Builds/build-picreact.sh # # Saida: artifacts/build/picreact-0be1cfd/picreact-0be1cfd-site.tar.gz (pasta site/); dois builds seguidos dao os mesmos bytes. set -euo pipefail COMMIT=0be1cfd8db7c35104b400394e52b76c66d9b35fc SHA256=534e878a96a4ba2c6bcf24f3c502c53e2d071219a188b83da7837f403a32aae8 NODE=24.21.0 here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" slug=picreact out="$root/artifacts/build/$slug-${COMMIT:0:7}" cache="$root/artifacts/build/cache" nodebin="$root/artifacts/tools/node-v$NODE/bin" [ -x "$nodebin/node" ] && [ "$("$nodebin/node" -v)" = "v$NODE" ] || { echo "Precisa do Node $NODE em $nodebin." >&2; exit 1; } export PATH="$nodebin:/usr/bin:/bin" mkdir -p "$cache" tarball="$cache/$slug-${COMMIT:0:7}.tar.gz" [ -f "$tarball" ] || curl -fsSL --retry 4 "https://github.com/bgonp/picreact/archive/$COMMIT.tar.gz" -o "$tarball" echo "$SHA256 $tarball" | shasum -a 256 -c - rm -rf "$out" mkdir -p "$out/tree" tar -xzf "$tarball" -C "$out/tree" --strip-components 1 cd "$out/tree" python3 -I - <<'PY' def edit(path, fn): s = open(path, encoding="utf-8").read() n = fn(s) assert n != s, path open(path, "w", encoding="utf-8", newline="").write(n) def rep(s, old, new): assert s.count(old) == 1, old return s.replace(old, new) def main(s): s = rep(s, "import { FC } from 'react'\n", "import { FC, useCallback, useEffect, useState } from 'react'\nimport { Router as WouterRouter } from 'wouter'\n") s = rep(s, "const Main: FC = () => (\n
", """// FredyGames: rotas por hash (#/play), a receita de hook do wouter 2 para hospedagem em pasta/iframe. const currentLocation = () => window.location.hash.replace(/^#/, '') || '/' const useHashLocation = (): [string, (to: string) => void] => { const [location, setLocation] = useState(currentLocation()) useEffect(() => { const handler = () => setLocation(currentLocation()) window.addEventListener('hashchange', handler) return () => window.removeEventListener('hashchange', handler) }, []) const navigate = useCallback((to: string) => { window.location.hash = to }, []) return [location, navigate] } const Main: FC = () => (
""") s = rep(s, "
\n)\n\nexport default Main", "
\n \n)\n\nexport default Main") return s edit("src/components/Main.tsx", main) def header(s): s = rep(s, "import ShareButton from 'components/ShareButton'\n", "") return rep(s, " \n", "") edit("src/components/Header.tsx", header) def play(s): s = rep(s, "import ShareButton from 'components/ShareButton'\n", "") return rep(s, " \n", "") edit("src/components/Play.tsx", play) def create(s): return rep(s, "'Puzzle created, now you can copy a direct link using share button'", "'Puzzle created, now you can solve it'") edit("src/components/Create.tsx", create) FOOTER = """import { FC } from 'react' import styles from 'styles/components/Footer.module.css' const Footer: FC = () => ( ) export default Footer """ edit("src/components/Footer.tsx", lambda s: FOOTER) PY rm -f src/components/ShareButton.tsx src/utils/share.ts export npm_config_cache="$cache/npm-$slug" npm_config_update_notifier=false npm ci --ignore-scripts --no-audit --no-fund --loglevel=error node node_modules/vite/bin/vite.js build --base=./ --outDir "$out/site" --emptyOutDir > "$out/build.log" 2>&1 || { tail -30 "$out/build.log"; exit 1; } if grep -rIlE "github\.com/bgonp|navigator\.share|copy-to-clipboard" "$out/site" --include=*.js --include=index.html | grep .; then echo "restou referencia a compartilhar/GitHub" >&2; exit 1; fi cp LICENSE "$out/site/LICENSE" cd "$out" find site -exec touch -t 202611010000 {} + find site -type f | LC_ALL=C sort > files.txt COPYFILE_DISABLE=1 tar --format=ustar --uid 0 --gid 0 --uname root --gname root -n -cf - -T files.txt | gzip -n -9 > "$slug-${COMMIT:0:7}-site.tar.gz" cat files.txt; rm -rf files.txt tree site build.log shasum -a 256 "$slug-${COMMIT:0:7}-site.tar.gz"