# ============================================================================= # deploy.yml — build every brand in this content repo with Hugo and deploy each # to .lilsus.fun via rsync over SSH. Runs on every push to main (i.e. # after each n8n pipeline run that commits repo//...). # # INSTALL: copy this file to .gitea/workflows/deploy.yml in the CONTENT repo # (lilsus/igaming-brands AND/OR lilsus/igaming-brands-i18n). # # REQUIRED Gitea secrets (repo → Settings → Actions → Secrets): # DEPLOY_SSH_KEY - private SSH key (PEM) whose public key is in the server's # authorized_keys for DEPLOY_USER. No passphrase. # DEPLOY_HOST - server hostname/IP that Caddy runs on (e.g. lilsus.fun) # DEPLOY_USER - ssh user with write access to DEPLOY_PATH # DEPLOY_PATH - base web root, e.g. /srv/www (brand goes to /) # The server's SSH host key is trusted on first connect (accept-new) and pinned # after, so no SSH_KNOWN_HOSTS secret is needed. For strict pinning instead, add # a SSH_KNOWN_HOSTS secret and change accept-new -> yes in the deploy step. # OPTIONAL Gitea variables (Settings → Actions → Variables): # TEMPLATE_REPO - clone URL of the template repo # (default: https://git.lilsus.fun/lilsus/igaming-template.git) # HUGO_VERSION - pinned Hugo Extended version (default 0.140.2) # SITE_TLD - base domain for per-brand hosts (default lilsus.fun) # # RUNNER REQUIREMENTS: a registered Gitea act_runner whose image has bash, git, # curl, tar, rsync, openssh-client and Node.js >= 20 (for Tailwind/PostCSS). # The default `gitea/runner`/`catthehacker/ubuntu` images include these. # ============================================================================= name: Build & Deploy Brands run-name: deploy ${{ gitea.sha }} on: push: branches: [main] paths: - "repo/**" - ".gitea/workflows/deploy.yml" workflow_dispatch: {} concurrency: group: deploy-${{ gitea.ref }} cancel-in-progress: false jobs: build-and-deploy: runs-on: ubuntu-latest env: TEMPLATE_REPO: ${{ vars.TEMPLATE_REPO || 'https://git.lilsus.fun/lilsus/igaming-template.git' }} HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.140.2' }} SITE_TLD: ${{ vars.SITE_TLD || 'lilsus.fun' }} CONTENT_DIR: ${{ gitea.workspace }}/content-repo TEMPLATE_DIR: ${{ gitea.workspace }}/template-repo HUGO_BIN: ${{ gitea.workspace }}/.hugo/hugo # Belt-and-braces: a single dropped internal-link target (e.g. a page the # pipeline QA rejected) must not fail the whole site build. Hugo maps this # top-level config key to HUGO_; "warning" downgrades unresolved # ref/relref from a fatal error to a warning. The generated hugo.toml also # sets this, so already-committed brands build even before regeneration. HUGO_REFLINKSERRORLEVEL: warning steps: # --- 1. Clone the content repo (this repo) directly. actions/checkout is # avoided so we don't depend on the runner reaching github.com. --- - name: Clone content repo run: | set -euo pipefail rm -rf "$CONTENT_DIR" git clone --depth 1 \ "${{ gitea.server_url }}/${{ gitea.repository }}.git" \ "$CONTENT_DIR" cd "$CONTENT_DIR" && git checkout -q "${{ gitea.sha }}" || true echo "Brands:"; ls -1 "$CONTENT_DIR/repo" || (echo "no repo/ dir" && exit 1) # --- 2. Clone the template (engine) repo. Public repo → no token needed. --- - name: Clone template repo run: | set -euo pipefail rm -rf "$TEMPLATE_DIR" git clone --depth 1 "$TEMPLATE_REPO" "$TEMPLATE_DIR" test -d "$TEMPLATE_DIR/layouts" || (echo "template has no layouts/" && exit 1) test -f "$TEMPLATE_DIR/data/locales.yaml" || echo "WARN: template missing data/locales.yaml" # --- 3. Install Hugo Extended (pinned) to match local verification. # Installed to a workspace path and invoked by absolute path # ($HUGO_BIN) so we don't depend on the runner's PATH-file behaviour. --- - name: Install Hugo Extended run: | set -euo pipefail # Pick the tarball matching the runner CPU architecture (the runner # image may be arm64, not amd64 — a mismatch gives "Exec format error"). case "$(uname -m)" in x86_64|amd64) HUGO_ARCH="linux-amd64" ;; aarch64|arm64) HUGO_ARCH="linux-arm64" ;; *) echo "unsupported arch: $(uname -m)"; exit 1 ;; esac HUGO_TARBALL="hugo_extended_${HUGO_VERSION}_${HUGO_ARCH}.tar.gz" URL="https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_TARBALL}" echo "Runner arch $(uname -m) -> downloading $URL" curl -fsSL --retry 3 -o /tmp/hugo.tar.gz "$URL" mkdir -p "$(dirname "$HUGO_BIN")" tar -xzf /tmp/hugo.tar.gz -C "$(dirname "$HUGO_BIN")" hugo "$HUGO_BIN" version # --- 4. Install Node deps once (Tailwind/PostCSS used by Hugo Pipes). # --ignore-scripts is critical: the template's hugo-extended # devDependency has a postinstall that downloads a ~50MB Hugo binary, # which hangs npm ci for minutes on arm64. We install Hugo ourselves # in step 3, so skipping lifecycle scripts is safe and much faster. # Tailwind v3 + PostCSS + autoprefixer are pure JS (no build step). --- - name: Install template Node deps run: | set -euo pipefail node --version cd "$TEMPLATE_DIR" if [ -f package-lock.json ]; then npm ci --no-audit --no-fund --ignore-scripts else npm install --no-audit --no-fund --ignore-scripts fi # --- 5. Prepare the SSH deploy key for rsync. Host key is trusted on # first connect and pinned thereafter (StrictHostKeyChecking=accept-new # in the deploy step), so no SSH_KNOWN_HOSTS secret is required. --- - name: Configure SSH run: | set -euo pipefail mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh" printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > "$HOME/.ssh/deploy_key" chmod 600 "$HOME/.ssh/deploy_key" touch "$HOME/.ssh/known_hosts" && chmod 644 "$HOME/.ssh/known_hosts" # --- 6. Build every brand and deploy it to .. --- - name: Build & deploy each brand run: | set -euo pipefail DEPLOY_HOST='${{ secrets.DEPLOY_HOST }}' DEPLOY_USER='${{ secrets.DEPLOY_USER }}' DEPLOY_PATH='${{ secrets.DEPLOY_PATH }}' [ -n "$DEPLOY_HOST" ] && [ -n "$DEPLOY_USER" ] && [ -n "$DEPLOY_PATH" ] \ || { echo "Missing DEPLOY_HOST/USER/PATH secrets"; exit 1; } SSH_OPTS="-i $HOME/.ssh/deploy_key -o UserKnownHostsFile=$HOME/.ssh/known_hosts -o StrictHostKeyChecking=accept-new" for BRAND_PATH in "$CONTENT_DIR"/repo/*/; do BRAND="$(basename "$BRAND_PATH")" BASE_URL="https://${BRAND}.${SITE_TLD}/" BUILD_DIR="${{ gitea.workspace }}/build/${BRAND}" echo "::group::Build ${BRAND} -> ${BASE_URL}" node "$TEMPLATE_DIR/scripts/ci-build-brand.mjs" \ --template "$TEMPLATE_DIR" \ --brand-dir "$BRAND_PATH" \ --out "$BUILD_DIR" # node_modules must be reachable from the build dir for Hugo PostCSS. ln -sfn "$TEMPLATE_DIR/node_modules" "$BUILD_DIR/node_modules" "$HUGO_BIN" --source "$BUILD_DIR" --gc --minify --baseURL "$BASE_URL" test -f "$BUILD_DIR/public/index.html" || { echo "build produced no index.html"; exit 1; } echo "::endgroup::" echo "::group::Deploy ${BRAND}" # Ensure the target dir exists, then mirror public/ into it. ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p '${DEPLOY_PATH}/${BRAND}'" rsync -az --delete \ -e "ssh $SSH_OPTS" \ "$BUILD_DIR/public/" \ "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/${BRAND}/" echo "Deployed ${BRAND} to ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/${BRAND}/" echo "::endgroup::" done - name: Cleanup SSH key if: always() run: rm -f "$HOME/.ssh/deploy_key"