#!/usr/bin/env bash # Compila o Mah (paciencia mahjong em HTML5, de ffalt, MIT; Angular 22) a partir do commit d17af16 (v2.1.1) de # github.com/ffalt/mah, com o "npm ci" do package-lock.json do autor (hashes de integridade do npm) e # "ng build --configuration production --base-href ./", com o Node 24.21.0 de artifacts/tools. # # Configuracao de build nossa (custom-build-config.json, o mecanismo do proprio projeto): nome "Mah", com o desafio # diario e o editor de layouts ligados e o recurso Kyodai desligado (ele carrega imagens de um site de terceiros). # # Mudancas no projeto, todas feitas aqui e visiveis neste script (cada uma falha se o texto esperado nao existir): # - os conjuntos de pecas que nao tem licenca livre clara ou que o autor nao distribui como livres saem da lista e # do pacote: ficam riichi, riichi-old, birds, space (CC BY 4.0) e uni, unib (dominio publico) e as variantes # pretas deles; saem animals (so uso pessoal), cheshire137, recri2 e open-fruits (sem licenca explicita), # picasso, classic e modern (GPL-3.0 de Thomas Ullrich: seriam obra combinada sob GPL) e o kyodai-extra.png # (pecas do Kyodai, jogo comercial); # - dos fundos ficam os tres do proprio autor (bamboo, blueclouds, grayclouds, CC BY 4.0) e o padroes do # pattern.monster (MIT); saem os cinco de fotografias do Unsplash (grass-1, stones-1, wood, wood-grain-1, space); # - KyodaiTileSets (lista de enderecos do web.archive.org) fica vazia e o codigo, que so roda com o recurso Kyodai # ligado (desligado aqui), deixa de apontar para o kyodai-extra.png; # - uma Content-Security-Policy no index.html bloqueia qualquer conexao fora da propria origem. # # src/FredGames.Bundler/Builds/build-mah.sh # # Saida: artifacts/build/mah-d17af16/{mah-d17af16-dist.tar.gz,licenses.txt}; o tar.gz (pasta dist/) tem que sair igual # em dois builds seguidos. licenses.txt e o 3rdpartylicenses.txt que o proprio Angular gera (todas as bibliotecas # que entram no pacote). set -euo pipefail COMMIT=d17af1619028508d8e68763a1c3fa37cad02af7a SHA256=de420257b4e15aff9bd8267f38883dc1bfb321c0fd31f3b1d2edd547dde5fe2f NODE=24.21.0 here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" out="$root/artifacts/build/mah-${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:$PATH" 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/ffalt/mah/archive/$COMMIT.tar.gz" "mah-${COMMIT:0:7}.tar.gz" "$SHA256" rm -rf "$out" mkdir -p "$out/tree" tar -xzf "$cache/mah-${COMMIT:0:7}.tar.gz" -C "$out/tree" --strip-components 1 cd "$out/tree" python3 -I - <<'PY' import os, re def edit(path, fn): s = open(path, encoding="utf-8").read() n = fn(s) assert n != s, path open(path, "w", encoding="utf-8").write(n) KEEP_SETS = {"riichi", "riichi-old", "birds", "space", "uni", "unib"} DROP_BACK = {"grass-1", "stones-1", "wood", "wood-grain-1", "space"} def consts(s): def keep_set(m): return m.group(0) if m.group(1) in KEEP_SETS else "" s2 = re.sub(r"\t\{ id: '([a-z0-9-]+)', type: '(?:SVG|PNG)', name: '[^']*', license: '[a-z]+' \},?\n", keep_set, s) assert s2 != s # a lista termina sem virgula no ultimo item s2 = re.sub(r",\n\];\n\nexport const ImageSetDefault", "\n];\n\nexport const ImageSetDefault", s2) def drop_back(m): return "" if m.group(1) in DROP_BACK else m.group(0) s3 = re.sub(r"\t\{ img: '([a-z0-9-]+)', name: 'BACK_[A-Z_]+', license: 'free' \},\n", drop_back, s2) assert s3 != s2 return s3 edit("src/app/model/consts.ts", consts) def kyodai(s): a = s.index("export const KyodaiTileSets: Array = [") b = s.index("\n];\n", a) + 4 n = s[:a] + "// FredyGames: the Kyodai tile sets (images on a third-party site) are not offered in this build.\nexport const KyodaiTileSets: Array = [];\n" + s[b:] assert "assets/svg/kyodai-extra.png" in n return n.replace("assets/svg/kyodai-extra.png", "") edit("src/app/model/tilesets.ts", kyodai) def csp(s): tag = '' assert tag in s return s.replace(tag, tag + "\n\t") edit("src/index.html", csp) # remaining references check import subprocess for pat in ("picasso", "open-fruits", "cheshire137", "recri2", "kyodai-extra"): pass PY ( cd src/assets/svg && rm -f animals* cheshire137* classic* modern* picasso* open-fruits* recri2* kyodai-extra.png ) ( cd src/assets/img && rm -f grass-1.jpg stones-1.jpg wood.jpg wood-grain-1.jpg space.jpg ) ls src/assets/svg src/assets/img if grep -rIn --include='*.ts' -E "'(picasso|modern|classic|animals|open-fruits|cheshire137|recri2)'|grass-1|stones-1|wood-grain-1" src/app | grep -v '\.spec\.ts'; then echo "restou referencia a arquivo removido" >&2; exit 1 fi echo '{"name":"Mah","title":"Mah","category":"Solitaire Board Game","description":"Mahjong solitaire","editor":true,"daily":true}' > custom-build-config.json export npm_config_cache="$cache/npm-mah" npm_config_update_notifier=false npm ci --ignore-scripts --no-audit --no-fund --loglevel=error npx ng build --configuration production --aot --base-href "./" > "$out/build.log" 2>&1 || { tail -30 "$out/build.log"; exit 1; } [ -f dist/index.html ] && [ -f dist/3rdpartylicenses.txt ] if grep -rIl --exclude=README.md -E "web\.archive\.org|kyodaimahjongg|unsplash" dist | grep .; then echo "restou endereco externo de imagem" >&2; exit 1; fi cp dist/3rdpartylicenses.txt "$out/licenses.txt" cp -R dist "$out/dist" find "$out/dist" -exec touch -t 202611010000 {} + ( cd "$out" && find dist -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 > "mah-${COMMIT:0:7}-dist.tar.gz" && rm files.txt ) cd "$out" rm -rf "$out/tree" ls -l "mah-${COMMIT:0:7}-dist.tar.gz" shasum -a 256 "mah-${COMMIT:0:7}-dist.tar.gz" licenses.txt