diff --git a/scripts/taler-merchant/selfbuild-webui.sh b/scripts/taler-merchant/selfbuild-webui.sh new file mode 100755 index 0000000..bd1cfda --- /dev/null +++ b/scripts/taler-merchant/selfbuild-webui.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# Build taler-merchant-webui SPA *inside* this container and install it. +# +# Usage (as root in taler-hacktivism): +# selfbuild-webui.sh [REF] +# selfbuild-webui.sh v1.6.11 +# selfbuild-webui.sh 0e4807845fc06f7c705711974650d67ef30e7919 +# WEBUI_REF=master selfbuild-webui.sh +# +# Env: +# WEBUI_REF tag / branch / commit (default: first arg or v1.6.11) +# TTC_GIT default https://git.taler.net/taler-typescript-core.git +# TTC_DIR default /var/taler-src/taler-typescript-core +# SPA_DEST default /usr/share/taler-merchant-webui +# PNPM_VERSION default 10.33.4 +# SKIP_NODE_INSTALL 1 = do not apt/npm install node/pnpm +# SKIP_DEPLOY 1 = build only, leave dist/prod in tree +# EXPECT_VERSION if set, die unless dist version.txt matches +# +set -euo pipefail + +export DEBIAN_FRONTEND=noninteractive +export PATH="/usr/local/bin:/usr/bin:/bin:${HOME}/.local/bin:${PATH:-}" + +WEBUI_REF="${WEBUI_REF:-${1:-v1.6.11}}" +TTC_GIT="${TTC_GIT:-https://git.taler.net/taler-typescript-core.git}" +TTC_DIR="${TTC_DIR:-/var/taler-src/taler-typescript-core}" +SPA_DEST="${SPA_DEST:-/usr/share/taler-merchant-webui}" +PNPM_VERSION="${PNPM_VERSION:-10.33.4}" +SKIP_NODE_INSTALL="${SKIP_NODE_INSTALL:-0}" +SKIP_DEPLOY="${SKIP_DEPLOY:-0}" +EXPECT_VERSION="${EXPECT_VERSION:-}" + +log() { printf '+ %s\n' "$*"; } +die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; } + +[ "$(id -u)" -eq 0 ] || die "run as root in the merchant container" + +# --------------------------------------------------------------------------- +# Toolchain: node + pnpm +# --------------------------------------------------------------------------- +ensure_node() { + if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1; then + log "node $(node -v) npm $(npm -v)" + return 0 + fi + [ "${SKIP_NODE_INSTALL}" = "1" ] && die "node/npm missing and SKIP_NODE_INSTALL=1" + + log "install nodejs + npm via apt" + apt-get update -qq + apt-get install -y --no-install-recommends nodejs npm ca-certificates git \ + python3 curl ca-certificates 2>&1 | tail -20 + command -v node >/dev/null 2>&1 || die "node still missing after apt" + log "node $(node -v) npm $(npm -v)" +} + +ensure_pnpm() { + if command -v pnpm >/dev/null 2>&1; then + log "pnpm $(pnpm -v)" + return 0 + fi + [ "${SKIP_NODE_INSTALL}" = "1" ] && die "pnpm missing and SKIP_NODE_INSTALL=1" + + log "install pnpm@${PNPM_VERSION} globally" + npm install -g "pnpm@${PNPM_VERSION}" 2>&1 | tail -15 + # npm global bin may be /usr/local/bin or under /usr/lib + hash -r 2>/dev/null || true + export PATH="$(npm prefix -g)/bin:${PATH}" + command -v pnpm >/dev/null 2>&1 || die "pnpm still missing after npm install -g" + log "pnpm $(pnpm -v)" +} + +# --------------------------------------------------------------------------- +# Source tree at REF +# --------------------------------------------------------------------------- +checkout_ttc() { + mkdir -p /var/taler-src + if [ -d "${TTC_DIR}/.git" ]; then + log "update existing ${TTC_DIR}" + cd "${TTC_DIR}" + git remote set-url origin "${TTC_GIT}" 2>/dev/null \ + || git remote add origin "${TTC_GIT}" || true + git fetch --tags --force origin + else + log "clone ${TTC_GIT} → ${TTC_DIR}" + rm -rf "${TTC_DIR}" + git clone "${TTC_GIT}" "${TTC_DIR}" + cd "${TTC_DIR}" + fi + + log "checkout ${WEBUI_REF}" + # Accept tag, branch, or raw commit + if git rev-parse --verify --quiet "refs/tags/${WEBUI_REF}" >/dev/null; then + git checkout -f "refs/tags/${WEBUI_REF}" + elif git rev-parse --verify --quiet "origin/${WEBUI_REF}" >/dev/null; then + git checkout -f -B "${WEBUI_REF}" "origin/${WEBUI_REF}" + elif git rev-parse --verify --quiet "${WEBUI_REF}" >/dev/null; then + git checkout -f "${WEBUI_REF}" + else + # maybe short hash only available after fetch + git fetch origin "${WEBUI_REF}" 2>/dev/null || true + git checkout -f "${WEBUI_REF}" || die "cannot checkout WEBUI_REF=${WEBUI_REF}" + fi + + # assets submodule (needed by build.mjs) + if [ -f .gitmodules ] && grep -q 'taler-assets' .gitmodules 2>/dev/null; then + log "submodule contrib/taler-assets" + git submodule update --init contrib/taler-assets \ + || git submodule update --init --recursive contrib/taler-assets \ + || true + fi + if [ ! -f contrib/taler-assets/svg/logo/qr-logo.svg ]; then + # fallback: shallow clone assets next to known relative url + log "assets missing — try direct clone" + rm -rf contrib/taler-assets + mkdir -p contrib + git clone --depth 1 https://git.taler.net/taler-assets.git contrib/taler-assets \ + || die "taler-assets incomplete" + fi + test -f contrib/taler-assets/svg/logo/qr-logo.svg || die "taler-assets still incomplete" + + HEAD_SHORT=$(git rev-parse --short HEAD) + HEAD_FULL=$(git rev-parse HEAD) + PKG_VER=$(python3 -c "import json; print(json.load(open('packages/taler-merchant-webui/package.json'))['version'])") + log "HEAD=${HEAD_SHORT} (${HEAD_FULL})" + log "package.json version=${PKG_VER}" +} + +# --------------------------------------------------------------------------- +# Build + install +# --------------------------------------------------------------------------- +build_spa() { + cd "${TTC_DIR}" + log "pnpm install + compile @gnu-taler/taler-merchant-webui" + pnpm install --frozen-lockfile --filter @gnu-taler/taler-merchant-webui... + pnpm run --filter @gnu-taler/taler-merchant-webui... compile + + PROD="${TTC_DIR}/packages/taler-merchant-webui/dist/prod" + test -f "${PROD}/index.js" || die "dist/prod/index.js missing" + test -f "${PROD}/version.txt" || die "dist/prod/version.txt missing" + GOT=$(tr -d ' \n\r' <"${PROD}/version.txt") + log "built version.txt=${GOT}" + if [ -n "${EXPECT_VERSION}" ] && [ "${GOT}" != "${EXPECT_VERSION}" ]; then + die "expected version ${EXPECT_VERSION}, got ${GOT}" + fi + # sensible default when building known tag + case "${WEBUI_REF}" in + v1.6.11|1.6.11) + [ "${GOT}" = "1.6.11" ] || die "expected 1.6.11 for ref ${WEBUI_REF}, got ${GOT}" + ;; + esac + BUILT_VER="${GOT}" +} + +deploy_spa() { + cd "${TTC_DIR}" + PROD="${TTC_DIR}/packages/taler-merchant-webui/dist/prod" + HEAD_SHORT=$(git rev-parse --short HEAD) + ts=$(date +%Y%m%d-%H%M%S) + + if [ -d "${SPA_DEST}" ] && [ "$(ls -A "${SPA_DEST}" 2>/dev/null || true)" ]; then + bak="${SPA_DEST}.bak-selfbuild-${ts}" + log "backup ${SPA_DEST} → ${bak}" + cp -a "${SPA_DEST}" "${bak}" + fi + mkdir -p "${SPA_DEST}" + find "${SPA_DEST}" -mindepth 1 -maxdepth 1 ! -name 'bak-*' -exec rm -rf {} + + + log "install SPA → ${SPA_DEST}" + cp -a "${PROD}/." "${SPA_DEST}/" + printf 'selfbuild-%s-%s\n' "${WEBUI_REF}" "${HEAD_SHORT}" >"${SPA_DEST}/version-overlay.txt" + chown -R root:root "${SPA_DEST}" + + echo "=== deployed ===" + cat "${SPA_DEST}/version.txt" + echo + cat "${SPA_DEST}/version-overlay.txt" + echo + wc -c "${SPA_DEST}/index.js" +} + +# --------------------------------------------------------------------------- +main() { + log "selfbuild-webui REF=${WEBUI_REF}" + ensure_node + ensure_pnpm + checkout_ttc + build_spa + if [ "${SKIP_DEPLOY}" = "1" ]; then + log "SKIP_DEPLOY=1 — left at ${TTC_DIR}/packages/taler-merchant-webui/dist/prod" + else + deploy_spa + fi + log "DONE webui ${WEBUI_REF} → version ${BUILT_VER:-?} @ ${SPA_DEST}" +} + +main "$@"